1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Linux cfg80211 driver scan related code
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 #include <typedefs.h>
25*4882a593Smuzhiyun #include <linuxver.h>
26*4882a593Smuzhiyun #include <osl.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 #include <802.11.h>
35*4882a593Smuzhiyun #include <bcmiov.h>
36*4882a593Smuzhiyun #include <linux/if_arp.h>
37*4882a593Smuzhiyun #include <asm/uaccess.h>
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun #include <ethernet.h>
40*4882a593Smuzhiyun #include <linux/kernel.h>
41*4882a593Smuzhiyun #include <linux/kthread.h>
42*4882a593Smuzhiyun #include <linux/netdevice.h>
43*4882a593Smuzhiyun #include <linux/sched.h>
44*4882a593Smuzhiyun #include <linux/etherdevice.h>
45*4882a593Smuzhiyun #include <linux/wireless.h>
46*4882a593Smuzhiyun #include <linux/ieee80211.h>
47*4882a593Smuzhiyun #include <linux/wait.h>
48*4882a593Smuzhiyun #if defined(CONFIG_TIZEN)
49*4882a593Smuzhiyun #include <linux/net_stat_tizen.h>
50*4882a593Smuzhiyun #endif /* CONFIG_TIZEN */
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_cfgscan.h>
59*4882a593Smuzhiyun #include <wl_cfgp2p.h>
60*4882a593Smuzhiyun #include <wl_cfgvif.h>
61*4882a593Smuzhiyun #include <bcmdevs.h>
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun #ifdef OEM_ANDROID
64*4882a593Smuzhiyun #include <wl_android.h>
65*4882a593Smuzhiyun #endif
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun #if defined(BCMDONGLEHOST)
68*4882a593Smuzhiyun #include <dngl_stats.h>
69*4882a593Smuzhiyun #include <dhd.h>
70*4882a593Smuzhiyun #include <dhd_linux.h>
71*4882a593Smuzhiyun #include <dhd_debug.h>
72*4882a593Smuzhiyun #include <dhdioctl.h>
73*4882a593Smuzhiyun #include <wlioctl.h>
74*4882a593Smuzhiyun #include <dhd_cfg80211.h>
75*4882a593Smuzhiyun #include <dhd_bus.h>
76*4882a593Smuzhiyun #include <wl_cfgvendor.h>
77*4882a593Smuzhiyun #endif /* defined(BCMDONGLEHOST) */
78*4882a593Smuzhiyun #ifdef BCMPCIE
79*4882a593Smuzhiyun #include <dhd_flowring.h>
80*4882a593Smuzhiyun #endif
81*4882a593Smuzhiyun #ifdef PNO_SUPPORT
82*4882a593Smuzhiyun #include <dhd_pno.h>
83*4882a593Smuzhiyun #endif /* PNO_SUPPORT */
84*4882a593Smuzhiyun #ifdef RTT_SUPPORT
85*4882a593Smuzhiyun #include "dhd_rtt.h"
86*4882a593Smuzhiyun #endif /* RTT_SUPPORT */
87*4882a593Smuzhiyun #include <dhd_config.h>
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun #define ACTIVE_SCAN 1
90*4882a593Smuzhiyun #define PASSIVE_SCAN 0
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun #define MIN_P2P_IE_LEN 8 /* p2p_ie->OUI(3) + p2p_ie->oui_type(1) +
93*4882a593Smuzhiyun * Attribute ID(1) + Length(2) + 1(Mininum length:1)
94*4882a593Smuzhiyun */
95*4882a593Smuzhiyun #define MAX_P2P_IE_LEN 251 /* Up To 251 */
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun #define WPS_ATTR_REQ_TYPE 0x103a
98*4882a593Smuzhiyun #define WPS_REQ_TYPE_ENROLLEE 0x01
99*4882a593Smuzhiyun #define SCAN_WAKE_LOCK_MARGIN_MS 500
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun #if defined(WL_CFG80211_P2P_DEV_IF)
102*4882a593Smuzhiyun #define CFG80211_READY_ON_CHANNEL(cfgdev, cookie, channel, channel_type, duration, flags) \
103*4882a593Smuzhiyun cfg80211_ready_on_channel(cfgdev, cookie, channel, duration, GFP_KERNEL);
104*4882a593Smuzhiyun #else
105*4882a593Smuzhiyun #define CFG80211_READY_ON_CHANNEL(cfgdev, cookie, channel, channel_type, duration, flags) \
106*4882a593Smuzhiyun cfg80211_ready_on_channel(cfgdev, cookie, channel, channel_type, duration, GFP_KERNEL);
107*4882a593Smuzhiyun #endif /* WL_CFG80211_P2P_DEV_IF */
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0))
110*4882a593Smuzhiyun #define CFG80211_SCHED_SCAN_STOPPED(wiphy, schedscan_req) \
111*4882a593Smuzhiyun cfg80211_sched_scan_stopped(wiphy, schedscan_req->reqid);
112*4882a593Smuzhiyun #else
113*4882a593Smuzhiyun #define CFG80211_SCHED_SCAN_STOPPED(wiphy, schedscan_req) \
114*4882a593Smuzhiyun cfg80211_sched_scan_stopped(wiphy);
115*4882a593Smuzhiyun #endif /* KERNEL > 4.11.0 */
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun #ifdef DHD_GET_VALID_CHANNELS
118*4882a593Smuzhiyun #define IS_DFS(chaninfo) ((chaninfo & WL_CHAN_RADAR) || \
119*4882a593Smuzhiyun (chaninfo & WL_CHAN_PASSIVE))
120*4882a593Smuzhiyun #endif /* DHD_GET_VALID_CHANNELS */
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun #if defined(USE_INITIAL_2G_SCAN) || defined(USE_INITIAL_SHORT_DWELL_TIME)
123*4882a593Smuzhiyun #define FIRST_SCAN_ACTIVE_DWELL_TIME_MS 40
124*4882a593Smuzhiyun bool g_first_broadcast_scan = TRUE;
125*4882a593Smuzhiyun #endif /* USE_INITIAL_2G_SCAN || USE_INITIAL_SHORT_DWELL_TIME */
126*4882a593Smuzhiyun #ifdef CUSTOMER_HW4_DEBUG
127*4882a593Smuzhiyun bool wl_scan_timeout_dbg_enabled = 0;
128*4882a593Smuzhiyun #endif /* CUSTOMER_HW4_DEBUG */
129*4882a593Smuzhiyun #ifdef P2P_LISTEN_OFFLOADING
130*4882a593Smuzhiyun void wl_cfg80211_cancel_p2plo(struct bcm_cfg80211 *cfg);
131*4882a593Smuzhiyun #endif /* P2P_LISTEN_OFFLOADING */
132*4882a593Smuzhiyun static void _wl_notify_scan_done(struct bcm_cfg80211 *cfg, bool aborted);
133*4882a593Smuzhiyun static s32 wl_notify_escan_complete(struct bcm_cfg80211 *cfg,
134*4882a593Smuzhiyun struct net_device *ndev, bool aborted);
135*4882a593Smuzhiyun void wl_cfgscan_scan_abort(struct bcm_cfg80211 *cfg);
136*4882a593Smuzhiyun static void _wl_cfgscan_cancel_scan(struct bcm_cfg80211 *cfg);
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun #ifdef ESCAN_CHANNEL_CACHE
139*4882a593Smuzhiyun void reset_roam_cache(struct bcm_cfg80211 *cfg);
140*4882a593Smuzhiyun void add_roam_cache(struct bcm_cfg80211 *cfg, wl_bss_info_t *bi);
141*4882a593Smuzhiyun int get_roam_channel_list(struct bcm_cfg80211 *cfg, chanspec_t target_chan, chanspec_t *channels,
142*4882a593Smuzhiyun int n_channels, const wlc_ssid_t *ssid, int ioctl_ver);
143*4882a593Smuzhiyun void set_roam_band(int band);
144*4882a593Smuzhiyun #endif /* ESCAN_CHANNEL_CACHE */
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun #ifdef ROAM_CHANNEL_CACHE
147*4882a593Smuzhiyun void print_roam_cache(struct bcm_cfg80211 *cfg);
148*4882a593Smuzhiyun #endif /* ROAM_CHANNEL_CACHE */
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun extern int passive_channel_skip;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun #ifdef DUAL_ESCAN_RESULT_BUFFER
153*4882a593Smuzhiyun static wl_scan_results_t *
wl_escan_get_buf(struct bcm_cfg80211 * cfg,bool aborted)154*4882a593Smuzhiyun wl_escan_get_buf(struct bcm_cfg80211 *cfg, bool aborted)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun u8 index;
157*4882a593Smuzhiyun if (aborted) {
158*4882a593Smuzhiyun if (cfg->escan_info.escan_type[0] == cfg->escan_info.escan_type[1]) {
159*4882a593Smuzhiyun index = (cfg->escan_info.cur_sync_id + 1)%SCAN_BUF_CNT;
160*4882a593Smuzhiyun } else {
161*4882a593Smuzhiyun index = (cfg->escan_info.cur_sync_id)%SCAN_BUF_CNT;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun } else {
164*4882a593Smuzhiyun index = (cfg->escan_info.cur_sync_id)%SCAN_BUF_CNT;
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun return (wl_scan_results_t *)cfg->escan_info.escan_buf[index];
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun static int
wl_escan_check_sync_id(struct bcm_cfg80211 * cfg,s32 status,u16 result_id,u16 wl_id)170*4882a593Smuzhiyun wl_escan_check_sync_id(struct bcm_cfg80211 *cfg, s32 status, u16 result_id, u16 wl_id)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun if (result_id != wl_id) {
173*4882a593Smuzhiyun WL_ERR(("ESCAN sync id mismatch :status :%d "
174*4882a593Smuzhiyun "cur_sync_id:%d coming sync_id:%d\n",
175*4882a593Smuzhiyun status, wl_id, result_id));
176*4882a593Smuzhiyun #ifdef DHD_SEND_HANG_ESCAN_SYNCID_MISMATCH
177*4882a593Smuzhiyun if (cfg->escan_info.prev_escan_aborted == FALSE) {
178*4882a593Smuzhiyun wl_cfg80211_handle_hang_event(bcmcfg_to_prmry_ndev(cfg),
179*4882a593Smuzhiyun HANG_REASON_ESCAN_SYNCID_MISMATCH, DUMP_TYPE_ESCAN_SYNCID_MISMATCH);
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun #endif /* DHD_SEND_HANG_ESCAN_SYNCID_MISMATCH */
182*4882a593Smuzhiyun return -1;
183*4882a593Smuzhiyun } else {
184*4882a593Smuzhiyun return 0;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun #define wl_escan_increment_sync_id(a, b) ((a)->escan_info.cur_sync_id += b)
188*4882a593Smuzhiyun #define wl_escan_init_sync_id(a) ((a)->escan_info.cur_sync_id = 0)
189*4882a593Smuzhiyun #else
190*4882a593Smuzhiyun #define wl_escan_get_buf(a, b) ((wl_scan_results_t *) (a)->escan_info.escan_buf)
191*4882a593Smuzhiyun #define wl_escan_check_sync_id(a, b, c, d) 0
192*4882a593Smuzhiyun #define wl_escan_increment_sync_id(a, b)
193*4882a593Smuzhiyun #define wl_escan_init_sync_id(a)
194*4882a593Smuzhiyun #endif /* DUAL_ESCAN_RESULT_BUFFER */
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun /*
197*4882a593Smuzhiyun * information element utilities
198*4882a593Smuzhiyun */
wl_rst_ie(struct bcm_cfg80211 * cfg)199*4882a593Smuzhiyun static void wl_rst_ie(struct bcm_cfg80211 *cfg)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun struct wl_ie *ie = wl_to_ie(cfg);
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun ie->offset = 0;
204*4882a593Smuzhiyun bzero(ie->buf, sizeof(ie->buf));
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
wl_update_hidden_ap_ie(wl_bss_info_t * bi,const u8 * ie_stream,u32 * ie_size,bool update_ssid)207*4882a593Smuzhiyun static void wl_update_hidden_ap_ie(wl_bss_info_t *bi, const u8 *ie_stream, u32 *ie_size,
208*4882a593Smuzhiyun bool update_ssid)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun u8 *ssidie;
211*4882a593Smuzhiyun int32 ssid_len = MIN(bi->SSID_len, DOT11_MAX_SSID_LEN);
212*4882a593Smuzhiyun int32 remaining_ie_buf_len, available_buffer_len, unused_buf_len;
213*4882a593Smuzhiyun /* cfg80211_find_ie defined in kernel returning const u8 */
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
216*4882a593Smuzhiyun ssidie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ie_stream, *ie_size);
217*4882a593Smuzhiyun GCC_DIAGNOSTIC_POP();
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun /* ERROR out if
220*4882a593Smuzhiyun * 1. No ssid IE is FOUND or
221*4882a593Smuzhiyun * 2. New ssid length is > what was allocated for existing ssid (as
222*4882a593Smuzhiyun * we do not want to overwrite the rest of the IEs) or
223*4882a593Smuzhiyun * 3. If in case of erroneous buffer input where ssid length doesnt match the space
224*4882a593Smuzhiyun * allocated to it.
225*4882a593Smuzhiyun */
226*4882a593Smuzhiyun if (!ssidie) {
227*4882a593Smuzhiyun return;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun available_buffer_len = ((int)(*ie_size)) - (ssidie + 2 - ie_stream);
230*4882a593Smuzhiyun remaining_ie_buf_len = available_buffer_len - (int)ssidie[1];
231*4882a593Smuzhiyun unused_buf_len = WL_EXTRA_BUF_MAX - (4 + bi->length + *ie_size);
232*4882a593Smuzhiyun if (ssidie[1] > available_buffer_len) {
233*4882a593Smuzhiyun WL_ERR_MEM(("wl_update_hidden_ap_ie: skip wl_update_hidden_ap_ie : overflow\n"));
234*4882a593Smuzhiyun return;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun /* ssidie[1] can be different with bi->SSID_len only if roaming status
238*4882a593Smuzhiyun * On scanning the values will be same each other.
239*4882a593Smuzhiyun */
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun if (ssidie[1] != ssid_len) {
242*4882a593Smuzhiyun if (ssidie[1]) {
243*4882a593Smuzhiyun WL_ERR_RLMT(("wl_update_hidden_ap_ie: Wrong SSID len: %d != %d\n",
244*4882a593Smuzhiyun ssidie[1], bi->SSID_len));
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun /* ssidie[1] is 1 in beacon on CISCO hidden networks. */
247*4882a593Smuzhiyun /*
248*4882a593Smuzhiyun * The bss info in firmware gets updated from beacon and probe resp.
249*4882a593Smuzhiyun * In case of hidden network, the bss_info that got updated by beacon,
250*4882a593Smuzhiyun * will not carry SSID and this can result in cfg80211_get_bss not finding a match.
251*4882a593Smuzhiyun * so include the SSID element.
252*4882a593Smuzhiyun */
253*4882a593Smuzhiyun if ((update_ssid && (ssid_len > ssidie[1])) && (unused_buf_len > ssid_len)) {
254*4882a593Smuzhiyun WL_INFORM_MEM(("Changing the SSID Info.\n"));
255*4882a593Smuzhiyun memmove(ssidie + ssid_len + 2,
256*4882a593Smuzhiyun (ssidie + 2) + ssidie[1],
257*4882a593Smuzhiyun remaining_ie_buf_len);
258*4882a593Smuzhiyun memcpy(ssidie + 2, bi->SSID, ssid_len);
259*4882a593Smuzhiyun *ie_size = *ie_size + ssid_len - ssidie[1];
260*4882a593Smuzhiyun ssidie[1] = ssid_len;
261*4882a593Smuzhiyun } else if (ssid_len < ssidie[1]) {
262*4882a593Smuzhiyun WL_ERR_MEM(("wl_update_hidden_ap_ie: Invalid SSID len: %d < %d\n",
263*4882a593Smuzhiyun bi->SSID_len, ssidie[1]));
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun return;
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun if (*(ssidie + 2) == '\0')
268*4882a593Smuzhiyun memcpy(ssidie + 2, bi->SSID, ssid_len);
269*4882a593Smuzhiyun return;
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun
wl_mrg_ie(struct bcm_cfg80211 * cfg,u8 * ie_stream,u16 ie_size)272*4882a593Smuzhiyun static s32 wl_mrg_ie(struct bcm_cfg80211 *cfg, u8 *ie_stream, u16 ie_size)
273*4882a593Smuzhiyun {
274*4882a593Smuzhiyun struct wl_ie *ie = wl_to_ie(cfg);
275*4882a593Smuzhiyun s32 err = 0;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun if (unlikely(ie->offset + ie_size > WL_TLV_INFO_MAX)) {
278*4882a593Smuzhiyun WL_ERR(("ei_stream crosses buffer boundary\n"));
279*4882a593Smuzhiyun return -ENOSPC;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun memcpy(&ie->buf[ie->offset], ie_stream, ie_size);
282*4882a593Smuzhiyun ie->offset += ie_size;
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun return err;
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
wl_cp_ie(struct bcm_cfg80211 * cfg,u8 * dst,u16 dst_size)287*4882a593Smuzhiyun static s32 wl_cp_ie(struct bcm_cfg80211 *cfg, u8 *dst, u16 dst_size)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun struct wl_ie *ie = wl_to_ie(cfg);
290*4882a593Smuzhiyun s32 err = 0;
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun if (unlikely(ie->offset > dst_size)) {
293*4882a593Smuzhiyun WL_ERR(("dst_size is not enough\n"));
294*4882a593Smuzhiyun return -ENOSPC;
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun memcpy(dst, &ie->buf[0], ie->offset);
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun return err;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
wl_get_ielen(struct bcm_cfg80211 * cfg)301*4882a593Smuzhiyun static u32 wl_get_ielen(struct bcm_cfg80211 *cfg)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun struct wl_ie *ie = wl_to_ie(cfg);
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun return ie->offset;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
wl_inform_single_bss(struct bcm_cfg80211 * cfg,wl_bss_info_t * bi,bool update_ssid)308*4882a593Smuzhiyun s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, wl_bss_info_t *bi, bool update_ssid)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun struct wiphy *wiphy = bcmcfg_to_wiphy(cfg);
311*4882a593Smuzhiyun struct ieee80211_mgmt *mgmt;
312*4882a593Smuzhiyun struct ieee80211_channel *channel;
313*4882a593Smuzhiyun struct wl_cfg80211_bss_info *notif_bss_info;
314*4882a593Smuzhiyun struct wl_scan_req *sr = wl_to_sr(cfg);
315*4882a593Smuzhiyun struct beacon_proberesp *beacon_proberesp;
316*4882a593Smuzhiyun struct cfg80211_bss *cbss = NULL;
317*4882a593Smuzhiyun dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
318*4882a593Smuzhiyun log_conn_event_t *event_data = NULL;
319*4882a593Smuzhiyun tlv_log *tlv_data = NULL;
320*4882a593Smuzhiyun u32 alloc_len;
321*4882a593Smuzhiyun u32 payload_len;
322*4882a593Smuzhiyun s32 mgmt_type;
323*4882a593Smuzhiyun s32 signal;
324*4882a593Smuzhiyun u32 freq;
325*4882a593Smuzhiyun s32 err = 0;
326*4882a593Smuzhiyun gfp_t aflags;
327*4882a593Smuzhiyun u8 tmp_buf[IEEE80211_MAX_SSID_LEN + 1];
328*4882a593Smuzhiyun chanspec_t chanspec;
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun if (unlikely(dtoh32(bi->length) > WL_BSS_INFO_MAX)) {
331*4882a593Smuzhiyun WL_DBG(("Beacon is larger than buffer. Discarding\n"));
332*4882a593Smuzhiyun return err;
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun if (bi->SSID_len > IEEE80211_MAX_SSID_LEN) {
336*4882a593Smuzhiyun WL_ERR(("wrong SSID len:%d\n", bi->SSID_len));
337*4882a593Smuzhiyun return -EINVAL;
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun aflags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
341*4882a593Smuzhiyun notif_bss_info = (struct wl_cfg80211_bss_info *)MALLOCZ(cfg->osh,
342*4882a593Smuzhiyun sizeof(*notif_bss_info) + sizeof(*mgmt) - sizeof(u8) + WL_BSS_INFO_MAX);
343*4882a593Smuzhiyun if (unlikely(!notif_bss_info)) {
344*4882a593Smuzhiyun WL_ERR(("notif_bss_info alloc failed\n"));
345*4882a593Smuzhiyun return -ENOMEM;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun /* Check for all currently supported bands */
348*4882a593Smuzhiyun if (!(
349*4882a593Smuzhiyun #ifdef WL_6G_BAND
350*4882a593Smuzhiyun CHSPEC_IS6G(bi->chanspec) ||
351*4882a593Smuzhiyun #endif /* WL_6G_BAND */
352*4882a593Smuzhiyun CHSPEC_IS5G(bi->chanspec) || CHSPEC_IS2G(bi->chanspec))) {
353*4882a593Smuzhiyun WL_ERR(("No valid band"));
354*4882a593Smuzhiyun MFREE(cfg->osh, notif_bss_info, sizeof(*notif_bss_info)
355*4882a593Smuzhiyun + sizeof(*mgmt) - sizeof(u8) + WL_BSS_INFO_MAX);
356*4882a593Smuzhiyun return -EINVAL;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun mgmt = (struct ieee80211_mgmt *)notif_bss_info->frame_buf;
360*4882a593Smuzhiyun chanspec = wl_chspec_driver_to_host(bi->chanspec);
361*4882a593Smuzhiyun notif_bss_info->channel = wf_chspec_ctlchan(chanspec);
362*4882a593Smuzhiyun notif_bss_info->band = CHSPEC_BAND(bi->chanspec);
363*4882a593Smuzhiyun notif_bss_info->rssi = dtoh16(bi->RSSI);
364*4882a593Smuzhiyun #if defined(RSSIAVG)
365*4882a593Smuzhiyun notif_bss_info->rssi = wl_get_avg_rssi(&cfg->g_rssi_cache_ctrl, &bi->BSSID);
366*4882a593Smuzhiyun if (notif_bss_info->rssi == RSSI_MINVAL)
367*4882a593Smuzhiyun notif_bss_info->rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
368*4882a593Smuzhiyun #endif
369*4882a593Smuzhiyun #if defined(RSSIOFFSET)
370*4882a593Smuzhiyun notif_bss_info->rssi = wl_update_rssi_offset(bcmcfg_to_prmry_ndev(cfg), notif_bss_info->rssi);
371*4882a593Smuzhiyun #endif
372*4882a593Smuzhiyun #if !defined(RSSIAVG) && !defined(RSSIOFFSET)
373*4882a593Smuzhiyun // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS
374*4882a593Smuzhiyun notif_bss_info->rssi = MIN(notif_bss_info->rssi, RSSI_MAXVAL);
375*4882a593Smuzhiyun #endif
376*4882a593Smuzhiyun memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN);
377*4882a593Smuzhiyun mgmt_type = cfg->active_scan ?
378*4882a593Smuzhiyun IEEE80211_STYPE_PROBE_RESP : IEEE80211_STYPE_BEACON;
379*4882a593Smuzhiyun if (!memcmp(bi->SSID, sr->ssid.SSID, bi->SSID_len)) {
380*4882a593Smuzhiyun mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | mgmt_type);
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun beacon_proberesp = cfg->active_scan ?
383*4882a593Smuzhiyun (struct beacon_proberesp *)&mgmt->u.probe_resp :
384*4882a593Smuzhiyun (struct beacon_proberesp *)&mgmt->u.beacon;
385*4882a593Smuzhiyun beacon_proberesp->timestamp = 0;
386*4882a593Smuzhiyun beacon_proberesp->beacon_int = cpu_to_le16(bi->beacon_period);
387*4882a593Smuzhiyun beacon_proberesp->capab_info = cpu_to_le16(bi->capability);
388*4882a593Smuzhiyun wl_rst_ie(cfg);
389*4882a593Smuzhiyun wl_update_hidden_ap_ie(bi, ((u8 *) bi) + bi->ie_offset, &bi->ie_length, update_ssid);
390*4882a593Smuzhiyun wl_mrg_ie(cfg, ((u8 *) bi) + bi->ie_offset, bi->ie_length);
391*4882a593Smuzhiyun wl_cp_ie(cfg, beacon_proberesp->variable, WL_BSS_INFO_MAX -
392*4882a593Smuzhiyun offsetof(struct wl_cfg80211_bss_info, frame_buf));
393*4882a593Smuzhiyun notif_bss_info->frame_len = offsetof(struct ieee80211_mgmt,
394*4882a593Smuzhiyun u.beacon.variable) + wl_get_ielen(cfg);
395*4882a593Smuzhiyun freq = wl_channel_to_frequency(notif_bss_info->channel, notif_bss_info->band);
396*4882a593Smuzhiyun if (freq == 0) {
397*4882a593Smuzhiyun WL_ERR(("Invalid channel, failed to change channel to freq\n"));
398*4882a593Smuzhiyun MFREE(cfg->osh, notif_bss_info, sizeof(*notif_bss_info)
399*4882a593Smuzhiyun + sizeof(*mgmt) - sizeof(u8) + WL_BSS_INFO_MAX);
400*4882a593Smuzhiyun return -EINVAL;
401*4882a593Smuzhiyun }
402*4882a593Smuzhiyun channel = ieee80211_get_channel(wiphy, freq);
403*4882a593Smuzhiyun memcpy(tmp_buf, bi->SSID, bi->SSID_len);
404*4882a593Smuzhiyun tmp_buf[bi->SSID_len] = '\0';
405*4882a593Smuzhiyun WL_SCAN(("BSSID %pM, channel %3d(%3d %3sMHz), rssi %3d, capa 0x%-4x, mgmt_type %d, "
406*4882a593Smuzhiyun "frame_len %3d, SSID \"%s\"\n",
407*4882a593Smuzhiyun &bi->BSSID, notif_bss_info->channel, CHSPEC_CHANNEL(chanspec),
408*4882a593Smuzhiyun CHSPEC_IS20(chanspec)?"20":
409*4882a593Smuzhiyun CHSPEC_IS40(chanspec)?"40":
410*4882a593Smuzhiyun CHSPEC_IS80(chanspec)?"80":
411*4882a593Smuzhiyun CHSPEC_IS160(chanspec)?"160":"??",
412*4882a593Smuzhiyun notif_bss_info->rssi, mgmt->u.beacon.capab_info, mgmt_type,
413*4882a593Smuzhiyun notif_bss_info->frame_len, tmp_buf));
414*4882a593Smuzhiyun if (unlikely(!channel)) {
415*4882a593Smuzhiyun WL_ERR(("ieee80211_get_channel error\n"));
416*4882a593Smuzhiyun MFREE(cfg->osh, notif_bss_info, sizeof(*notif_bss_info)
417*4882a593Smuzhiyun + sizeof(*mgmt) - sizeof(u8) + WL_BSS_INFO_MAX);
418*4882a593Smuzhiyun return -EINVAL;
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun signal = notif_bss_info->rssi * 100;
422*4882a593Smuzhiyun if (!mgmt->u.probe_resp.timestamp) {
423*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
424*4882a593Smuzhiyun struct osl_timespec ts;
425*4882a593Smuzhiyun osl_get_monotonic_boottime(&ts);
426*4882a593Smuzhiyun mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec*1000000)
427*4882a593Smuzhiyun + ts.tv_nsec / 1000;
428*4882a593Smuzhiyun #else
429*4882a593Smuzhiyun struct osl_timespec tv;
430*4882a593Smuzhiyun osl_do_gettimeofday(&tv);
431*4882a593Smuzhiyun mgmt->u.probe_resp.timestamp = ((u64)tv.tv_sec*1000000)
432*4882a593Smuzhiyun + tv.tv_usec;
433*4882a593Smuzhiyun #endif
434*4882a593Smuzhiyun }
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun cbss = cfg80211_inform_bss_frame(wiphy, channel, mgmt,
437*4882a593Smuzhiyun le16_to_cpu(notif_bss_info->frame_len), signal, aflags);
438*4882a593Smuzhiyun if (unlikely(!cbss)) {
439*4882a593Smuzhiyun WL_ERR(("cfg80211_inform_bss_frame error bssid " MACDBG " channel %d \n",
440*4882a593Smuzhiyun MAC2STRDBG((u8*)(&bi->BSSID)), notif_bss_info->channel));
441*4882a593Smuzhiyun err = -EINVAL;
442*4882a593Smuzhiyun goto out_err;
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun CFG80211_PUT_BSS(wiphy, cbss);
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun if (DBG_RING_ACTIVE(dhdp, DHD_EVENT_RING_ID) &&
448*4882a593Smuzhiyun (cfg->sched_scan_req && !cfg->scan_request)) {
449*4882a593Smuzhiyun alloc_len = sizeof(log_conn_event_t) + (3 * sizeof(tlv_log)) +
450*4882a593Smuzhiyun IEEE80211_MAX_SSID_LEN + sizeof(uint16) +
451*4882a593Smuzhiyun sizeof(int16);
452*4882a593Smuzhiyun event_data = (log_conn_event_t *)MALLOCZ(dhdp->osh, alloc_len);
453*4882a593Smuzhiyun if (!event_data) {
454*4882a593Smuzhiyun WL_ERR(("%s: failed to allocate the log_conn_event_t with "
455*4882a593Smuzhiyun "length(%d)\n", __func__, alloc_len));
456*4882a593Smuzhiyun goto out_err;
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun payload_len = sizeof(log_conn_event_t);
460*4882a593Smuzhiyun event_data->event = WIFI_EVENT_DRIVER_PNO_SCAN_RESULT_FOUND;
461*4882a593Smuzhiyun tlv_data = event_data->tlvs;
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun /* ssid */
464*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_SSID;
465*4882a593Smuzhiyun tlv_data->len = bi->SSID_len;
466*4882a593Smuzhiyun memcpy(tlv_data->value, bi->SSID, bi->SSID_len);
467*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
468*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun /* channel */
471*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_CHANNEL;
472*4882a593Smuzhiyun tlv_data->len = sizeof(uint16);
473*4882a593Smuzhiyun memcpy(tlv_data->value, ¬if_bss_info->channel, sizeof(uint16));
474*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
475*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun /* rssi */
478*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_RSSI;
479*4882a593Smuzhiyun tlv_data->len = sizeof(int16);
480*4882a593Smuzhiyun memcpy(tlv_data->value, ¬if_bss_info->rssi, sizeof(int16));
481*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
482*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun dhd_os_push_push_ring_data(dhdp, DHD_EVENT_RING_ID,
485*4882a593Smuzhiyun event_data, payload_len);
486*4882a593Smuzhiyun MFREE(dhdp->osh, event_data, alloc_len);
487*4882a593Smuzhiyun }
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun out_err:
490*4882a593Smuzhiyun MFREE(cfg->osh, notif_bss_info, sizeof(*notif_bss_info)
491*4882a593Smuzhiyun + sizeof(*mgmt) - sizeof(u8) + WL_BSS_INFO_MAX);
492*4882a593Smuzhiyun return err;
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun struct wireless_dev * wl_get_scan_wdev(struct bcm_cfg80211 *cfg);
496*4882a593Smuzhiyun struct net_device *
wl_get_scan_ndev(struct bcm_cfg80211 * cfg)497*4882a593Smuzhiyun wl_get_scan_ndev(struct bcm_cfg80211 *cfg)
498*4882a593Smuzhiyun {
499*4882a593Smuzhiyun struct wireless_dev *wdev = NULL;
500*4882a593Smuzhiyun struct net_device *ndev = NULL;
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun wdev = wl_get_scan_wdev(cfg);
503*4882a593Smuzhiyun if (!wdev) {
504*4882a593Smuzhiyun WL_ERR(("No wdev present\n"));
505*4882a593Smuzhiyun return NULL;
506*4882a593Smuzhiyun }
507*4882a593Smuzhiyun
508*4882a593Smuzhiyun ndev = wdev_to_wlc_ndev(wdev, cfg);
509*4882a593Smuzhiyun if (!ndev) {
510*4882a593Smuzhiyun WL_ERR(("No ndev present\n"));
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun return ndev;
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun #if defined(BSSCACHE) || defined(RSSIAVG)
wl_cfg80211_update_bss_cache(struct bcm_cfg80211 * cfg)517*4882a593Smuzhiyun void wl_cfg80211_update_bss_cache(struct bcm_cfg80211 *cfg)
518*4882a593Smuzhiyun {
519*4882a593Smuzhiyun #if defined(RSSIAVG)
520*4882a593Smuzhiyun struct net_device *ndev = wl_get_scan_ndev(cfg);
521*4882a593Smuzhiyun int rssi;
522*4882a593Smuzhiyun #endif
523*4882a593Smuzhiyun wl_scan_results_t *bss_list = cfg->bss_list;
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun /* Free cache in p2p scanning*/
526*4882a593Smuzhiyun if (p2p_is_on(cfg) && p2p_scan(cfg)) {
527*4882a593Smuzhiyun #if defined(RSSIAVG)
528*4882a593Smuzhiyun wl_free_rssi_cache(&cfg->g_rssi_cache_ctrl);
529*4882a593Smuzhiyun #endif
530*4882a593Smuzhiyun #if defined(BSSCACHE)
531*4882a593Smuzhiyun wl_free_bss_cache(&cfg->g_bss_cache_ctrl);
532*4882a593Smuzhiyun #endif
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun /* Update cache */
536*4882a593Smuzhiyun #if defined(RSSIAVG)
537*4882a593Smuzhiyun wl_update_rssi_cache(&cfg->g_rssi_cache_ctrl, bss_list);
538*4882a593Smuzhiyun if (!in_atomic() && ndev) {
539*4882a593Smuzhiyun wl_update_connected_rssi_cache(ndev, &cfg->g_rssi_cache_ctrl, &rssi);
540*4882a593Smuzhiyun }
541*4882a593Smuzhiyun #endif
542*4882a593Smuzhiyun #if defined(BSSCACHE)
543*4882a593Smuzhiyun wl_update_bss_cache(&cfg->g_bss_cache_ctrl,
544*4882a593Smuzhiyun #if defined(RSSIAVG)
545*4882a593Smuzhiyun &cfg->g_rssi_cache_ctrl,
546*4882a593Smuzhiyun #endif
547*4882a593Smuzhiyun bss_list);
548*4882a593Smuzhiyun #endif
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun /* delete dirty cache */
551*4882a593Smuzhiyun #if defined(RSSIAVG)
552*4882a593Smuzhiyun wl_delete_dirty_rssi_cache(&cfg->g_rssi_cache_ctrl);
553*4882a593Smuzhiyun wl_reset_rssi_cache(&cfg->g_rssi_cache_ctrl);
554*4882a593Smuzhiyun #endif
555*4882a593Smuzhiyun #if defined(BSSCACHE)
556*4882a593Smuzhiyun wl_delete_dirty_bss_cache(&cfg->g_bss_cache_ctrl);
557*4882a593Smuzhiyun wl_reset_bss_cache(&cfg->g_bss_cache_ctrl);
558*4882a593Smuzhiyun #endif
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun #endif
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun #if defined(BSSCACHE)
wl_inform_bss_cache(struct bcm_cfg80211 * cfg)564*4882a593Smuzhiyun s32 wl_inform_bss_cache(struct bcm_cfg80211 *cfg)
565*4882a593Smuzhiyun {
566*4882a593Smuzhiyun wl_scan_results_t *bss_list = cfg->bss_list;
567*4882a593Smuzhiyun wl_bss_info_t *bi = NULL; /* must be initialized */
568*4882a593Smuzhiyun s32 err = 0;
569*4882a593Smuzhiyun s32 i, cnt;
570*4882a593Smuzhiyun wl_bss_cache_t *node;
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun WL_SCAN(("scanned AP count (%d)\n", bss_list->count));
573*4882a593Smuzhiyun bss_list = cfg->bss_list;
574*4882a593Smuzhiyun preempt_disable();
575*4882a593Smuzhiyun bi = next_bss(bss_list, bi);
576*4882a593Smuzhiyun for_each_bss(bss_list, bi, i) {
577*4882a593Smuzhiyun err = wl_inform_single_bss(cfg, bi, false);
578*4882a593Smuzhiyun if (unlikely(err)) {
579*4882a593Smuzhiyun WL_ERR(("bss inform failed\n"));
580*4882a593Smuzhiyun }
581*4882a593Smuzhiyun }
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun cnt = i;
584*4882a593Smuzhiyun node = cfg->g_bss_cache_ctrl.m_cache_head;
585*4882a593Smuzhiyun WL_SCAN(("cached AP count (%d)\n", wl_bss_cache_size(&cfg->g_bss_cache_ctrl)));
586*4882a593Smuzhiyun for (i=cnt; node && i<WL_AP_MAX; i++) {
587*4882a593Smuzhiyun if (node->dirty > 1) {
588*4882a593Smuzhiyun bi = node->results.bss_info;
589*4882a593Smuzhiyun err = wl_inform_single_bss(cfg, bi, false);
590*4882a593Smuzhiyun }
591*4882a593Smuzhiyun node = node->next;
592*4882a593Smuzhiyun }
593*4882a593Smuzhiyun preempt_enable();
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun return err;
596*4882a593Smuzhiyun }
597*4882a593Smuzhiyun #endif
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun static s32
wl_inform_bss(struct bcm_cfg80211 * cfg)600*4882a593Smuzhiyun wl_inform_bss(struct bcm_cfg80211 *cfg)
601*4882a593Smuzhiyun {
602*4882a593Smuzhiyun #if !defined(BSSCACHE)
603*4882a593Smuzhiyun wl_scan_results_t *bss_list;
604*4882a593Smuzhiyun wl_bss_info_t *bi = NULL; /* must be initialized */
605*4882a593Smuzhiyun s32 i;
606*4882a593Smuzhiyun #endif
607*4882a593Smuzhiyun struct net_device *ndev = wl_get_scan_ndev(cfg);
608*4882a593Smuzhiyun s32 err = 0;
609*4882a593Smuzhiyun
610*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
611*4882a593Smuzhiyun if (ndev)
612*4882a593Smuzhiyun wl_ext_in4way_sync(ndev, 0, WL_EXT_STATUS_SCAN_COMPLETE, NULL);
613*4882a593Smuzhiyun #endif
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun #if defined(BSSCACHE) || defined(RSSIAVG)
616*4882a593Smuzhiyun wl_cfg80211_update_bss_cache(cfg);
617*4882a593Smuzhiyun #endif
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun #if defined(BSSCACHE)
620*4882a593Smuzhiyun err = wl_inform_bss_cache(cfg);
621*4882a593Smuzhiyun #else
622*4882a593Smuzhiyun bss_list = cfg->bss_list;
623*4882a593Smuzhiyun WL_SCAN(("scanned AP count (%d)\n", bss_list->count));
624*4882a593Smuzhiyun #ifdef ESCAN_CHANNEL_CACHE
625*4882a593Smuzhiyun reset_roam_cache(cfg);
626*4882a593Smuzhiyun #endif /* ESCAN_CHANNEL_CACHE */
627*4882a593Smuzhiyun preempt_disable();
628*4882a593Smuzhiyun bi = next_bss(bss_list, bi);
629*4882a593Smuzhiyun for_each_bss(bss_list, bi, i) {
630*4882a593Smuzhiyun #ifdef ESCAN_CHANNEL_CACHE
631*4882a593Smuzhiyun add_roam_cache(cfg, bi);
632*4882a593Smuzhiyun #endif /* ESCAN_CHANNEL_CACHE */
633*4882a593Smuzhiyun err = wl_inform_single_bss(cfg, bi, false);
634*4882a593Smuzhiyun if (unlikely(err)) {
635*4882a593Smuzhiyun WL_ERR(("bss inform failed\n"));
636*4882a593Smuzhiyun }
637*4882a593Smuzhiyun }
638*4882a593Smuzhiyun preempt_enable();
639*4882a593Smuzhiyun #endif
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun if (cfg->autochannel && ndev) {
642*4882a593Smuzhiyun #if defined(BSSCACHE)
643*4882a593Smuzhiyun wl_ext_get_best_channel(ndev, &cfg->g_bss_cache_ctrl,
644*4882a593Smuzhiyun &cfg->best_2g_ch, &cfg->best_5g_ch, &cfg->best_6g_ch);
645*4882a593Smuzhiyun #else
646*4882a593Smuzhiyun wl_ext_get_best_channel(ndev, bss_list,
647*4882a593Smuzhiyun &cfg->best_2g_ch, &cfg->best_5g_ch, &cfg->best_6g_ch);
648*4882a593Smuzhiyun #endif
649*4882a593Smuzhiyun }
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun WL_MEM(("cfg80211 scan cache updated\n"));
652*4882a593Smuzhiyun #ifdef ROAM_CHANNEL_CACHE
653*4882a593Smuzhiyun /* print_roam_cache(); */
654*4882a593Smuzhiyun update_roam_cache(cfg, ioctl_version);
655*4882a593Smuzhiyun #endif /* ROAM_CHANNEL_CACHE */
656*4882a593Smuzhiyun return err;
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun #ifdef WL11U
660*4882a593Smuzhiyun static bcm_tlv_t *
wl_cfg80211_find_interworking_ie(const u8 * parse,u32 len)661*4882a593Smuzhiyun wl_cfg80211_find_interworking_ie(const u8 *parse, u32 len)
662*4882a593Smuzhiyun {
663*4882a593Smuzhiyun bcm_tlv_t *ie;
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun /* unfortunately it's too much work to dispose the const cast - bcm_parse_tlvs
666*4882a593Smuzhiyun * is used everywhere and changing its prototype to take const qualifier needs
667*4882a593Smuzhiyun * a massive change to all its callers...
668*4882a593Smuzhiyun */
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun if ((ie = bcm_parse_tlvs(parse, len, DOT11_MNG_INTERWORKING_ID))) {
671*4882a593Smuzhiyun return ie;
672*4882a593Smuzhiyun }
673*4882a593Smuzhiyun return NULL;
674*4882a593Smuzhiyun }
675*4882a593Smuzhiyun
676*4882a593Smuzhiyun static s32
wl_cfg80211_clear_iw_ie(struct bcm_cfg80211 * cfg,struct net_device * ndev,s32 bssidx)677*4882a593Smuzhiyun wl_cfg80211_clear_iw_ie(struct bcm_cfg80211 *cfg, struct net_device *ndev, s32 bssidx)
678*4882a593Smuzhiyun {
679*4882a593Smuzhiyun ie_setbuf_t ie_setbuf;
680*4882a593Smuzhiyun
681*4882a593Smuzhiyun WL_DBG(("clear interworking IE\n"));
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun bzero(&ie_setbuf, sizeof(ie_setbuf_t));
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun ie_setbuf.ie_buffer.iecount = htod32(1);
686*4882a593Smuzhiyun ie_setbuf.ie_buffer.ie_list[0].ie_data.id = DOT11_MNG_INTERWORKING_ID;
687*4882a593Smuzhiyun ie_setbuf.ie_buffer.ie_list[0].ie_data.len = 0;
688*4882a593Smuzhiyun
689*4882a593Smuzhiyun return wldev_iovar_setbuf_bsscfg(ndev, "ie", &ie_setbuf, sizeof(ie_setbuf),
690*4882a593Smuzhiyun cfg->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &cfg->ioctl_buf_sync);
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun
693*4882a593Smuzhiyun static s32
wl_cfg80211_add_iw_ie(struct bcm_cfg80211 * cfg,struct net_device * ndev,s32 bssidx,s32 pktflag,uint8 ie_id,uint8 * data,uint8 data_len)694*4882a593Smuzhiyun wl_cfg80211_add_iw_ie(struct bcm_cfg80211 *cfg, struct net_device *ndev, s32 bssidx, s32 pktflag,
695*4882a593Smuzhiyun uint8 ie_id, uint8 *data, uint8 data_len)
696*4882a593Smuzhiyun {
697*4882a593Smuzhiyun s32 err = BCME_OK;
698*4882a593Smuzhiyun s32 buf_len;
699*4882a593Smuzhiyun ie_setbuf_t *ie_setbuf;
700*4882a593Smuzhiyun ie_getbuf_t ie_getbufp;
701*4882a593Smuzhiyun char getbuf[WLC_IOCTL_SMLEN];
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun if (ie_id != DOT11_MNG_INTERWORKING_ID) {
704*4882a593Smuzhiyun WL_ERR(("unsupported (id=%d)\n", ie_id));
705*4882a593Smuzhiyun return BCME_UNSUPPORTED;
706*4882a593Smuzhiyun }
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun /* access network options (1 octet) is the mandatory field */
709*4882a593Smuzhiyun if (!data || data_len == 0 || data_len > IW_IES_MAX_BUF_LEN) {
710*4882a593Smuzhiyun WL_ERR(("wrong interworking IE (len=%d)\n", data_len));
711*4882a593Smuzhiyun return BCME_BADARG;
712*4882a593Smuzhiyun }
713*4882a593Smuzhiyun
714*4882a593Smuzhiyun /* Validate the pktflag parameter */
715*4882a593Smuzhiyun if ((pktflag & ~(VNDR_IE_BEACON_FLAG | VNDR_IE_PRBRSP_FLAG |
716*4882a593Smuzhiyun VNDR_IE_ASSOCRSP_FLAG | VNDR_IE_AUTHRSP_FLAG |
717*4882a593Smuzhiyun VNDR_IE_PRBREQ_FLAG | VNDR_IE_ASSOCREQ_FLAG|
718*4882a593Smuzhiyun VNDR_IE_CUSTOM_FLAG))) {
719*4882a593Smuzhiyun WL_ERR(("invalid packet flag 0x%x\n", pktflag));
720*4882a593Smuzhiyun return BCME_BADARG;
721*4882a593Smuzhiyun }
722*4882a593Smuzhiyun
723*4882a593Smuzhiyun buf_len = sizeof(ie_setbuf_t) + data_len - 1;
724*4882a593Smuzhiyun
725*4882a593Smuzhiyun ie_getbufp.id = DOT11_MNG_INTERWORKING_ID;
726*4882a593Smuzhiyun if (wldev_iovar_getbuf_bsscfg(ndev, "ie", (void *)&ie_getbufp,
727*4882a593Smuzhiyun sizeof(ie_getbufp), getbuf, WLC_IOCTL_SMLEN, bssidx, &cfg->ioctl_buf_sync)
728*4882a593Smuzhiyun == BCME_OK) {
729*4882a593Smuzhiyun if (!memcmp(&getbuf[TLV_HDR_LEN], data, data_len)) {
730*4882a593Smuzhiyun WL_DBG(("skip to set interworking IE\n"));
731*4882a593Smuzhiyun return BCME_OK;
732*4882a593Smuzhiyun }
733*4882a593Smuzhiyun }
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun /* if already set with previous values, delete it first */
736*4882a593Smuzhiyun if (cfg->wl11u) {
737*4882a593Smuzhiyun if ((err = wl_cfg80211_clear_iw_ie(cfg, ndev, bssidx)) != BCME_OK) {
738*4882a593Smuzhiyun return err;
739*4882a593Smuzhiyun }
740*4882a593Smuzhiyun }
741*4882a593Smuzhiyun
742*4882a593Smuzhiyun ie_setbuf = (ie_setbuf_t *)MALLOCZ(cfg->osh, buf_len);
743*4882a593Smuzhiyun if (!ie_setbuf) {
744*4882a593Smuzhiyun WL_ERR(("Error allocating buffer for IE\n"));
745*4882a593Smuzhiyun return -ENOMEM;
746*4882a593Smuzhiyun }
747*4882a593Smuzhiyun strlcpy(ie_setbuf->cmd, "add", sizeof(ie_setbuf->cmd));
748*4882a593Smuzhiyun
749*4882a593Smuzhiyun /* Buffer contains only 1 IE */
750*4882a593Smuzhiyun ie_setbuf->ie_buffer.iecount = htod32(1);
751*4882a593Smuzhiyun /* use VNDR_IE_CUSTOM_FLAG flags for none vendor IE . currently fixed value */
752*4882a593Smuzhiyun ie_setbuf->ie_buffer.ie_list[0].pktflag = htod32(pktflag);
753*4882a593Smuzhiyun
754*4882a593Smuzhiyun /* Now, add the IE to the buffer */
755*4882a593Smuzhiyun ie_setbuf->ie_buffer.ie_list[0].ie_data.id = DOT11_MNG_INTERWORKING_ID;
756*4882a593Smuzhiyun ie_setbuf->ie_buffer.ie_list[0].ie_data.len = data_len;
757*4882a593Smuzhiyun /* Returning void here as max data_len can be 8 */
758*4882a593Smuzhiyun (void)memcpy_s((uchar *)&ie_setbuf->ie_buffer.ie_list[0].ie_data.data[0], sizeof(uint8),
759*4882a593Smuzhiyun data, data_len);
760*4882a593Smuzhiyun
761*4882a593Smuzhiyun if ((err = wldev_iovar_setbuf_bsscfg(ndev, "ie", ie_setbuf, buf_len,
762*4882a593Smuzhiyun cfg->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &cfg->ioctl_buf_sync))
763*4882a593Smuzhiyun == BCME_OK) {
764*4882a593Smuzhiyun WL_DBG(("set interworking IE\n"));
765*4882a593Smuzhiyun cfg->wl11u = TRUE;
766*4882a593Smuzhiyun err = wldev_iovar_setint_bsscfg(ndev, "grat_arp", 1, bssidx);
767*4882a593Smuzhiyun }
768*4882a593Smuzhiyun
769*4882a593Smuzhiyun MFREE(cfg->osh, ie_setbuf, buf_len);
770*4882a593Smuzhiyun return err;
771*4882a593Smuzhiyun }
772*4882a593Smuzhiyun #endif /* WL11U */
773*4882a593Smuzhiyun
774*4882a593Smuzhiyun #ifdef WL_BCNRECV
775*4882a593Smuzhiyun /* Beacon recv results handler sending to upper layer */
776*4882a593Smuzhiyun static s32
wl_bcnrecv_result_handler(struct bcm_cfg80211 * cfg,bcm_struct_cfgdev * cfgdev,wl_bss_info_v109_2_t * bi,uint32 scan_status)777*4882a593Smuzhiyun wl_bcnrecv_result_handler(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
778*4882a593Smuzhiyun wl_bss_info_v109_2_t *bi, uint32 scan_status)
779*4882a593Smuzhiyun {
780*4882a593Smuzhiyun s32 err = BCME_OK;
781*4882a593Smuzhiyun struct wiphy *wiphy = NULL;
782*4882a593Smuzhiyun wl_bcnrecv_result_t *bcn_recv = NULL;
783*4882a593Smuzhiyun struct osl_timespec ts;
784*4882a593Smuzhiyun if (!bi) {
785*4882a593Smuzhiyun WL_ERR(("%s: bi is NULL\n", __func__));
786*4882a593Smuzhiyun err = BCME_NORESOURCE;
787*4882a593Smuzhiyun goto exit;
788*4882a593Smuzhiyun }
789*4882a593Smuzhiyun if ((bi->length - bi->ie_length) < sizeof(wl_bss_info_v109_2_t)) {
790*4882a593Smuzhiyun WL_ERR(("bi info version doesn't support bcn_recv attributes\n"));
791*4882a593Smuzhiyun goto exit;
792*4882a593Smuzhiyun }
793*4882a593Smuzhiyun
794*4882a593Smuzhiyun if (scan_status == WLC_E_STATUS_RXBCN) {
795*4882a593Smuzhiyun wiphy = cfg->wdev->wiphy;
796*4882a593Smuzhiyun if (!wiphy) {
797*4882a593Smuzhiyun WL_ERR(("wiphy is NULL\n"));
798*4882a593Smuzhiyun err = BCME_NORESOURCE;
799*4882a593Smuzhiyun goto exit;
800*4882a593Smuzhiyun }
801*4882a593Smuzhiyun bcn_recv = (wl_bcnrecv_result_t *)MALLOCZ(cfg->osh, sizeof(*bcn_recv));
802*4882a593Smuzhiyun if (unlikely(!bcn_recv)) {
803*4882a593Smuzhiyun WL_ERR(("Failed to allocate memory\n"));
804*4882a593Smuzhiyun return -ENOMEM;
805*4882a593Smuzhiyun }
806*4882a593Smuzhiyun /* Returning void here as copy size does not exceed dest size of SSID */
807*4882a593Smuzhiyun (void)memcpy_s((char *)bcn_recv->SSID, DOT11_MAX_SSID_LEN,
808*4882a593Smuzhiyun (char *)bi->SSID, DOT11_MAX_SSID_LEN);
809*4882a593Smuzhiyun /* Returning void here as copy size does not exceed dest size of ETH_LEN */
810*4882a593Smuzhiyun (void)memcpy_s(&bcn_recv->BSSID, ETHER_ADDR_LEN, &bi->BSSID, ETH_ALEN);
811*4882a593Smuzhiyun bcn_recv->channel = wf_chspec_ctlchan(
812*4882a593Smuzhiyun wl_chspec_driver_to_host(bi->chanspec));
813*4882a593Smuzhiyun bcn_recv->beacon_interval = bi->beacon_period;
814*4882a593Smuzhiyun
815*4882a593Smuzhiyun /* kernal timestamp */
816*4882a593Smuzhiyun osl_get_monotonic_boottime(&ts);
817*4882a593Smuzhiyun bcn_recv->system_time = ((u64)ts.tv_sec*1000000)
818*4882a593Smuzhiyun + ts.tv_nsec / 1000;
819*4882a593Smuzhiyun bcn_recv->timestamp[0] = bi->timestamp[0];
820*4882a593Smuzhiyun bcn_recv->timestamp[1] = bi->timestamp[1];
821*4882a593Smuzhiyun if ((err = wl_android_bcnrecv_event(cfgdev_to_wlc_ndev(cfgdev, cfg),
822*4882a593Smuzhiyun BCNRECV_ATTR_BCNINFO, 0, 0,
823*4882a593Smuzhiyun (uint8 *)bcn_recv, sizeof(*bcn_recv)))
824*4882a593Smuzhiyun != BCME_OK) {
825*4882a593Smuzhiyun WL_ERR(("failed to send bcnrecv event, error:%d\n", err));
826*4882a593Smuzhiyun }
827*4882a593Smuzhiyun } else {
828*4882a593Smuzhiyun WL_DBG(("Ignoring Escan Event:%d \n", scan_status));
829*4882a593Smuzhiyun }
830*4882a593Smuzhiyun exit:
831*4882a593Smuzhiyun if (bcn_recv) {
832*4882a593Smuzhiyun MFREE(cfg->osh, bcn_recv, sizeof(*bcn_recv));
833*4882a593Smuzhiyun }
834*4882a593Smuzhiyun return err;
835*4882a593Smuzhiyun }
836*4882a593Smuzhiyun #endif /* WL_BCNRECV */
837*4882a593Smuzhiyun
838*4882a593Smuzhiyun #ifdef ESCAN_BUF_OVERFLOW_MGMT
839*4882a593Smuzhiyun #ifndef WL_DRV_AVOID_SCANCACHE
840*4882a593Smuzhiyun static void
wl_cfg80211_find_removal_candidate(wl_bss_info_t * bss,removal_element_t * candidate)841*4882a593Smuzhiyun wl_cfg80211_find_removal_candidate(wl_bss_info_t *bss, removal_element_t *candidate)
842*4882a593Smuzhiyun {
843*4882a593Smuzhiyun int idx;
844*4882a593Smuzhiyun for (idx = 0; idx < BUF_OVERFLOW_MGMT_COUNT; idx++) {
845*4882a593Smuzhiyun int len = BUF_OVERFLOW_MGMT_COUNT - idx - 1;
846*4882a593Smuzhiyun if (bss->RSSI < candidate[idx].RSSI) {
847*4882a593Smuzhiyun if (len) {
848*4882a593Smuzhiyun /* In the below memcpy operation the candidate array always has the
849*4882a593Smuzhiyun * buffer space available to max 'len' calculated in the for loop.
850*4882a593Smuzhiyun */
851*4882a593Smuzhiyun (void)memcpy_s(&candidate[idx + 1],
852*4882a593Smuzhiyun (sizeof(removal_element_t) * len),
853*4882a593Smuzhiyun &candidate[idx], sizeof(removal_element_t) * len);
854*4882a593Smuzhiyun }
855*4882a593Smuzhiyun candidate[idx].RSSI = bss->RSSI;
856*4882a593Smuzhiyun candidate[idx].length = bss->length;
857*4882a593Smuzhiyun (void)memcpy_s(&candidate[idx].BSSID, ETHER_ADDR_LEN,
858*4882a593Smuzhiyun &bss->BSSID, ETHER_ADDR_LEN);
859*4882a593Smuzhiyun return;
860*4882a593Smuzhiyun }
861*4882a593Smuzhiyun }
862*4882a593Smuzhiyun }
863*4882a593Smuzhiyun
864*4882a593Smuzhiyun static void
wl_cfg80211_remove_lowRSSI_info(wl_scan_results_t * list,removal_element_t * candidate,wl_bss_info_t * bi)865*4882a593Smuzhiyun wl_cfg80211_remove_lowRSSI_info(wl_scan_results_t *list, removal_element_t *candidate,
866*4882a593Smuzhiyun wl_bss_info_t *bi)
867*4882a593Smuzhiyun {
868*4882a593Smuzhiyun int idx1, idx2;
869*4882a593Smuzhiyun int total_delete_len = 0;
870*4882a593Smuzhiyun for (idx1 = 0; idx1 < BUF_OVERFLOW_MGMT_COUNT; idx1++) {
871*4882a593Smuzhiyun int cur_len = WL_SCAN_RESULTS_FIXED_SIZE;
872*4882a593Smuzhiyun wl_bss_info_t *bss = NULL;
873*4882a593Smuzhiyun if (candidate[idx1].RSSI >= bi->RSSI)
874*4882a593Smuzhiyun continue;
875*4882a593Smuzhiyun for (idx2 = 0; idx2 < list->count; idx2++) {
876*4882a593Smuzhiyun bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) :
877*4882a593Smuzhiyun list->bss_info;
878*4882a593Smuzhiyun if (!bss) {
879*4882a593Smuzhiyun continue;
880*4882a593Smuzhiyun }
881*4882a593Smuzhiyun if (!bcmp(&candidate[idx1].BSSID, &bss->BSSID, ETHER_ADDR_LEN) &&
882*4882a593Smuzhiyun candidate[idx1].RSSI == bss->RSSI &&
883*4882a593Smuzhiyun candidate[idx1].length == dtoh32(bss->length)) {
884*4882a593Smuzhiyun u32 delete_len = dtoh32(bss->length);
885*4882a593Smuzhiyun WL_DBG(("delete scan info of " MACDBG " to add new AP\n",
886*4882a593Smuzhiyun MAC2STRDBG(bss->BSSID.octet)));
887*4882a593Smuzhiyun if (idx2 < list->count -1) {
888*4882a593Smuzhiyun memmove((u8 *)bss, (u8 *)bss + delete_len,
889*4882a593Smuzhiyun list->buflen - cur_len - delete_len);
890*4882a593Smuzhiyun }
891*4882a593Smuzhiyun list->buflen -= delete_len;
892*4882a593Smuzhiyun list->count--;
893*4882a593Smuzhiyun total_delete_len += delete_len;
894*4882a593Smuzhiyun /* if delete_len is greater than or equal to result length */
895*4882a593Smuzhiyun if (total_delete_len >= bi->length) {
896*4882a593Smuzhiyun return;
897*4882a593Smuzhiyun }
898*4882a593Smuzhiyun break;
899*4882a593Smuzhiyun }
900*4882a593Smuzhiyun cur_len += dtoh32(bss->length);
901*4882a593Smuzhiyun }
902*4882a593Smuzhiyun }
903*4882a593Smuzhiyun }
904*4882a593Smuzhiyun #endif /* WL_DRV_AVOID_SCANCACHE */
905*4882a593Smuzhiyun #endif /* ESCAN_BUF_OVERFLOW_MGMT */
906*4882a593Smuzhiyun
907*4882a593Smuzhiyun s32
wl_escan_handler(struct bcm_cfg80211 * cfg,bcm_struct_cfgdev * cfgdev,const wl_event_msg_t * e,void * data)908*4882a593Smuzhiyun wl_escan_handler(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
909*4882a593Smuzhiyun const wl_event_msg_t *e, void *data)
910*4882a593Smuzhiyun {
911*4882a593Smuzhiyun s32 err = BCME_OK;
912*4882a593Smuzhiyun s32 status = ntoh32(e->status);
913*4882a593Smuzhiyun wl_escan_result_t *escan_result;
914*4882a593Smuzhiyun struct net_device *ndev = NULL;
915*4882a593Smuzhiyun #ifndef WL_DRV_AVOID_SCANCACHE
916*4882a593Smuzhiyun wl_bss_info_t *bi;
917*4882a593Smuzhiyun u32 bi_length;
918*4882a593Smuzhiyun const wifi_p2p_ie_t * p2p_ie;
919*4882a593Smuzhiyun const u8 *p2p_dev_addr = NULL;
920*4882a593Smuzhiyun wl_scan_results_t *list;
921*4882a593Smuzhiyun wl_bss_info_t *bss = NULL;
922*4882a593Smuzhiyun u32 i;
923*4882a593Smuzhiyun #endif /* WL_DRV_AVOID_SCANCACHE */
924*4882a593Smuzhiyun u16 channel;
925*4882a593Smuzhiyun struct ieee80211_supported_band *band;
926*4882a593Smuzhiyun
927*4882a593Smuzhiyun WL_DBG((" enter event type : %d, status : %d \n",
928*4882a593Smuzhiyun ntoh32(e->event_type), ntoh32(e->status)));
929*4882a593Smuzhiyun
930*4882a593Smuzhiyun ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);
931*4882a593Smuzhiyun
932*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
933*4882a593Smuzhiyun
934*4882a593Smuzhiyun if (cfg->loc.in_progress) {
935*4882a593Smuzhiyun /* Listen in progress */
936*4882a593Smuzhiyun if ((status == WLC_E_STATUS_SUCCESS) || (status == WLC_E_STATUS_ABORT)) {
937*4882a593Smuzhiyun if (delayed_work_pending(&cfg->loc.work)) {
938*4882a593Smuzhiyun cancel_delayed_work_sync(&cfg->loc.work);
939*4882a593Smuzhiyun }
940*4882a593Smuzhiyun err = wl_cfgscan_notify_listen_complete(cfg);
941*4882a593Smuzhiyun goto exit;
942*4882a593Smuzhiyun } else {
943*4882a593Smuzhiyun WL_DBG(("Listen in progress. Unknown status. %d\n", status));
944*4882a593Smuzhiyun }
945*4882a593Smuzhiyun }
946*4882a593Smuzhiyun
947*4882a593Smuzhiyun /* P2P SCAN is coming from primary interface */
948*4882a593Smuzhiyun if (wl_get_p2p_status(cfg, SCANNING)) {
949*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, SENDING_ACT_FRM))
950*4882a593Smuzhiyun ndev = cfg->afx_hdl->dev;
951*4882a593Smuzhiyun else
952*4882a593Smuzhiyun ndev = cfg->escan_info.ndev;
953*4882a593Smuzhiyun }
954*4882a593Smuzhiyun escan_result = (wl_escan_result_t *)data;
955*4882a593Smuzhiyun if (!escan_result) {
956*4882a593Smuzhiyun WL_ERR(("Invalid escan result (NULL data)\n"));
957*4882a593Smuzhiyun goto exit;
958*4882a593Smuzhiyun }
959*4882a593Smuzhiyun #ifdef WL_BCNRECV
960*4882a593Smuzhiyun if (status == WLC_E_STATUS_RXBCN) {
961*4882a593Smuzhiyun if (cfg->bcnrecv_info.bcnrecv_state == BEACON_RECV_STARTED) {
962*4882a593Smuzhiyun /* handle beacon recv scan results */
963*4882a593Smuzhiyun wl_bss_info_v109_2_t *bi_info;
964*4882a593Smuzhiyun bi_info = (wl_bss_info_v109_2_t *)escan_result->bss_info;
965*4882a593Smuzhiyun err = wl_bcnrecv_result_handler(cfg, cfgdev, bi_info, status);
966*4882a593Smuzhiyun } else {
967*4882a593Smuzhiyun WL_ERR(("ignore bcnrx event in disabled state(%d)\n",
968*4882a593Smuzhiyun cfg->bcnrecv_info.bcnrecv_state));
969*4882a593Smuzhiyun }
970*4882a593Smuzhiyun goto exit;
971*4882a593Smuzhiyun }
972*4882a593Smuzhiyun #endif /* WL_BCNRECV */
973*4882a593Smuzhiyun if (!ndev || (!wl_get_drv_status(cfg, SCANNING, ndev) && !cfg->sched_scan_running)) {
974*4882a593Smuzhiyun WL_ERR_RLMT(("escan is not ready. drv_scan_status 0x%x"
975*4882a593Smuzhiyun " e_type %d e_status %d\n",
976*4882a593Smuzhiyun wl_get_drv_status(cfg, SCANNING, ndev),
977*4882a593Smuzhiyun ntoh32(e->event_type), ntoh32(e->status)));
978*4882a593Smuzhiyun goto exit;
979*4882a593Smuzhiyun }
980*4882a593Smuzhiyun
981*4882a593Smuzhiyun #ifndef WL_DRV_AVOID_SCANCACHE
982*4882a593Smuzhiyun if (wl_escan_check_sync_id(cfg, status, escan_result->sync_id,
983*4882a593Smuzhiyun cfg->escan_info.cur_sync_id) < 0) {
984*4882a593Smuzhiyun goto exit;
985*4882a593Smuzhiyun }
986*4882a593Smuzhiyun
987*4882a593Smuzhiyun if (status == WLC_E_STATUS_PARTIAL) {
988*4882a593Smuzhiyun WL_DBG(("WLC_E_STATUS_PARTIAL \n"));
989*4882a593Smuzhiyun DBG_EVENT_LOG((dhd_pub_t *)cfg->pub, WIFI_EVENT_DRIVER_SCAN_RESULT_FOUND);
990*4882a593Smuzhiyun if ((dtoh32(escan_result->buflen) > (int)ESCAN_BUF_SIZE) ||
991*4882a593Smuzhiyun (dtoh32(escan_result->buflen) < sizeof(wl_escan_result_t))) {
992*4882a593Smuzhiyun WL_ERR(("Invalid escan buffer len:%d\n", dtoh32(escan_result->buflen)));
993*4882a593Smuzhiyun goto exit;
994*4882a593Smuzhiyun }
995*4882a593Smuzhiyun if (dtoh16(escan_result->bss_count) != 1) {
996*4882a593Smuzhiyun WL_ERR(("Invalid bss_count %d: ignoring\n", escan_result->bss_count));
997*4882a593Smuzhiyun goto exit;
998*4882a593Smuzhiyun }
999*4882a593Smuzhiyun bi = escan_result->bss_info;
1000*4882a593Smuzhiyun if (!bi) {
1001*4882a593Smuzhiyun WL_ERR(("Invalid escan bss info (NULL pointer)\n"));
1002*4882a593Smuzhiyun goto exit;
1003*4882a593Smuzhiyun }
1004*4882a593Smuzhiyun bi_length = dtoh32(bi->length);
1005*4882a593Smuzhiyun if (bi_length != (dtoh32(escan_result->buflen) - WL_ESCAN_RESULTS_FIXED_SIZE)) {
1006*4882a593Smuzhiyun WL_ERR(("Invalid bss_info length %d: ignoring\n", bi_length));
1007*4882a593Smuzhiyun goto exit;
1008*4882a593Smuzhiyun }
1009*4882a593Smuzhiyun
1010*4882a593Smuzhiyun /* +++++ terence 20130524: skip invalid bss */
1011*4882a593Smuzhiyun channel =
1012*4882a593Smuzhiyun bi->ctl_ch ? bi->ctl_ch : CHSPEC_CHANNEL(wl_chspec_driver_to_host(bi->chanspec));
1013*4882a593Smuzhiyun if (channel <= CH_MAX_2G_CHANNEL)
1014*4882a593Smuzhiyun band = bcmcfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
1015*4882a593Smuzhiyun else
1016*4882a593Smuzhiyun band = bcmcfg_to_wiphy(cfg)->bands[IEEE80211_BAND_5GHZ];
1017*4882a593Smuzhiyun if (!band) {
1018*4882a593Smuzhiyun WL_ERR(("No valid band\n"));
1019*4882a593Smuzhiyun goto exit;
1020*4882a593Smuzhiyun }
1021*4882a593Smuzhiyun if (!dhd_conf_match_channel(cfg->pub, channel))
1022*4882a593Smuzhiyun goto exit;
1023*4882a593Smuzhiyun /* ----- terence 20130524: skip invalid bss */
1024*4882a593Smuzhiyun
1025*4882a593Smuzhiyun if (!(bcmcfg_to_wiphy(cfg)->interface_modes & BIT(NL80211_IFTYPE_ADHOC))) {
1026*4882a593Smuzhiyun if (dtoh16(bi->capability) & DOT11_CAP_IBSS) {
1027*4882a593Smuzhiyun WL_DBG(("Ignoring IBSS result\n"));
1028*4882a593Smuzhiyun goto exit;
1029*4882a593Smuzhiyun }
1030*4882a593Smuzhiyun }
1031*4882a593Smuzhiyun
1032*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, FINDING_COMMON_CHANNEL)) {
1033*4882a593Smuzhiyun p2p_dev_addr = wl_cfgp2p_retreive_p2p_dev_addr(bi, bi_length);
1034*4882a593Smuzhiyun if (p2p_dev_addr && !memcmp(p2p_dev_addr,
1035*4882a593Smuzhiyun cfg->afx_hdl->tx_dst_addr.octet, ETHER_ADDR_LEN)) {
1036*4882a593Smuzhiyun s32 channel = wf_chspec_ctlchan(
1037*4882a593Smuzhiyun wl_chspec_driver_to_host(bi->chanspec));
1038*4882a593Smuzhiyun
1039*4882a593Smuzhiyun if ((channel > MAXCHANNEL) || (channel <= 0))
1040*4882a593Smuzhiyun channel = WL_INVALID;
1041*4882a593Smuzhiyun else
1042*4882a593Smuzhiyun WL_ERR(("ACTION FRAME SCAN : Peer " MACDBG " found,"
1043*4882a593Smuzhiyun " channel : %d\n",
1044*4882a593Smuzhiyun MAC2STRDBG(cfg->afx_hdl->tx_dst_addr.octet),
1045*4882a593Smuzhiyun channel));
1046*4882a593Smuzhiyun
1047*4882a593Smuzhiyun wl_clr_p2p_status(cfg, SCANNING);
1048*4882a593Smuzhiyun cfg->afx_hdl->peer_chan = channel;
1049*4882a593Smuzhiyun complete(&cfg->act_frm_scan);
1050*4882a593Smuzhiyun goto exit;
1051*4882a593Smuzhiyun }
1052*4882a593Smuzhiyun
1053*4882a593Smuzhiyun } else {
1054*4882a593Smuzhiyun int cur_len = WL_SCAN_RESULTS_FIXED_SIZE;
1055*4882a593Smuzhiyun #ifdef ESCAN_BUF_OVERFLOW_MGMT
1056*4882a593Smuzhiyun removal_element_t candidate[BUF_OVERFLOW_MGMT_COUNT];
1057*4882a593Smuzhiyun int remove_lower_rssi = FALSE;
1058*4882a593Smuzhiyun
1059*4882a593Smuzhiyun bzero(candidate, sizeof(removal_element_t)*BUF_OVERFLOW_MGMT_COUNT);
1060*4882a593Smuzhiyun #endif /* ESCAN_BUF_OVERFLOW_MGMT */
1061*4882a593Smuzhiyun
1062*4882a593Smuzhiyun list = wl_escan_get_buf(cfg, FALSE);
1063*4882a593Smuzhiyun if (scan_req_match(cfg)) {
1064*4882a593Smuzhiyun #ifdef WL_HOST_BAND_MGMT
1065*4882a593Smuzhiyun s32 channel_band = 0;
1066*4882a593Smuzhiyun chanspec_t chspec;
1067*4882a593Smuzhiyun #endif /* WL_HOST_BAND_MGMT */
1068*4882a593Smuzhiyun /* p2p scan && allow only probe response */
1069*4882a593Smuzhiyun if ((cfg->p2p->search_state != WL_P2P_DISC_ST_SCAN) &&
1070*4882a593Smuzhiyun (bi->flags & WL_BSS_FLAGS_FROM_BEACON))
1071*4882a593Smuzhiyun goto exit;
1072*4882a593Smuzhiyun if ((p2p_ie = wl_cfgp2p_find_p2pie(((u8 *) bi) + bi->ie_offset,
1073*4882a593Smuzhiyun bi->ie_length)) == NULL) {
1074*4882a593Smuzhiyun WL_ERR(("Couldn't find P2PIE in probe"
1075*4882a593Smuzhiyun " response/beacon\n"));
1076*4882a593Smuzhiyun goto exit;
1077*4882a593Smuzhiyun }
1078*4882a593Smuzhiyun #ifdef WL_HOST_BAND_MGMT
1079*4882a593Smuzhiyun chspec = wl_chspec_driver_to_host(bi->chanspec);
1080*4882a593Smuzhiyun channel_band = CHSPEC2WLC_BAND(chspec);
1081*4882a593Smuzhiyun
1082*4882a593Smuzhiyun if ((
1083*4882a593Smuzhiyun #ifdef WL_6G_BAND
1084*4882a593Smuzhiyun (cfg->curr_band == WLC_BAND_6G) ||
1085*4882a593Smuzhiyun #endif /* WL_6G_BAND */
1086*4882a593Smuzhiyun (cfg->curr_band == WLC_BAND_5G)) &&
1087*4882a593Smuzhiyun (channel_band == WLC_BAND_2G)) {
1088*4882a593Smuzhiyun /* Avoid sending the GO results in band conflict */
1089*4882a593Smuzhiyun if (wl_cfgp2p_retreive_p2pattrib(p2p_ie,
1090*4882a593Smuzhiyun P2P_SEID_GROUP_ID) != NULL)
1091*4882a593Smuzhiyun goto exit;
1092*4882a593Smuzhiyun }
1093*4882a593Smuzhiyun #endif /* WL_HOST_BAND_MGMT */
1094*4882a593Smuzhiyun }
1095*4882a593Smuzhiyun #ifdef ESCAN_BUF_OVERFLOW_MGMT
1096*4882a593Smuzhiyun if (bi_length > ESCAN_BUF_SIZE - list->buflen)
1097*4882a593Smuzhiyun remove_lower_rssi = TRUE;
1098*4882a593Smuzhiyun #endif /* ESCAN_BUF_OVERFLOW_MGMT */
1099*4882a593Smuzhiyun
1100*4882a593Smuzhiyun for (i = 0; i < list->count; i++) {
1101*4882a593Smuzhiyun bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length))
1102*4882a593Smuzhiyun : list->bss_info;
1103*4882a593Smuzhiyun if (!bss) {
1104*4882a593Smuzhiyun WL_ERR(("bss is NULL\n"));
1105*4882a593Smuzhiyun goto exit;
1106*4882a593Smuzhiyun }
1107*4882a593Smuzhiyun #ifdef ESCAN_BUF_OVERFLOW_MGMT
1108*4882a593Smuzhiyun WL_DBG(("%s("MACDBG"), i=%d bss: RSSI %d list->count %d\n",
1109*4882a593Smuzhiyun bss->SSID, MAC2STRDBG(bss->BSSID.octet),
1110*4882a593Smuzhiyun i, bss->RSSI, list->count));
1111*4882a593Smuzhiyun
1112*4882a593Smuzhiyun if (remove_lower_rssi)
1113*4882a593Smuzhiyun wl_cfg80211_find_removal_candidate(bss, candidate);
1114*4882a593Smuzhiyun #endif /* ESCAN_BUF_OVERFLOW_MGMT */
1115*4882a593Smuzhiyun
1116*4882a593Smuzhiyun if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) &&
1117*4882a593Smuzhiyun (CHSPEC_BAND(wl_chspec_driver_to_host(bi->chanspec))
1118*4882a593Smuzhiyun == CHSPEC_BAND(wl_chspec_driver_to_host(bss->chanspec))) &&
1119*4882a593Smuzhiyun bi->SSID_len == bss->SSID_len &&
1120*4882a593Smuzhiyun !bcmp(bi->SSID, bss->SSID, bi->SSID_len)) {
1121*4882a593Smuzhiyun
1122*4882a593Smuzhiyun /* do not allow beacon data to update
1123*4882a593Smuzhiyun *the data recd from a probe response
1124*4882a593Smuzhiyun */
1125*4882a593Smuzhiyun if (!(bss->flags & WL_BSS_FLAGS_FROM_BEACON) &&
1126*4882a593Smuzhiyun (bi->flags & WL_BSS_FLAGS_FROM_BEACON))
1127*4882a593Smuzhiyun goto exit;
1128*4882a593Smuzhiyun
1129*4882a593Smuzhiyun WL_DBG(("%s("MACDBG"), i=%d prev: RSSI %d"
1130*4882a593Smuzhiyun " flags 0x%x, new: RSSI %d flags 0x%x\n",
1131*4882a593Smuzhiyun bss->SSID, MAC2STRDBG(bi->BSSID.octet), i,
1132*4882a593Smuzhiyun bss->RSSI, bss->flags, bi->RSSI, bi->flags));
1133*4882a593Smuzhiyun
1134*4882a593Smuzhiyun if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) ==
1135*4882a593Smuzhiyun (bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL)) {
1136*4882a593Smuzhiyun /* preserve max RSSI if the measurements are
1137*4882a593Smuzhiyun * both on-channel or both off-channel
1138*4882a593Smuzhiyun */
1139*4882a593Smuzhiyun WL_DBG(("%s("MACDBG"), same onchan"
1140*4882a593Smuzhiyun ", RSSI: prev %d new %d\n",
1141*4882a593Smuzhiyun bss->SSID, MAC2STRDBG(bi->BSSID.octet),
1142*4882a593Smuzhiyun bss->RSSI, bi->RSSI));
1143*4882a593Smuzhiyun bi->RSSI = MAX(bss->RSSI, bi->RSSI);
1144*4882a593Smuzhiyun } else if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) &&
1145*4882a593Smuzhiyun (bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) == 0) {
1146*4882a593Smuzhiyun /* preserve the on-channel rssi measurement
1147*4882a593Smuzhiyun * if the new measurement is off channel
1148*4882a593Smuzhiyun */
1149*4882a593Smuzhiyun WL_DBG(("%s("MACDBG"), prev onchan"
1150*4882a593Smuzhiyun ", RSSI: prev %d new %d\n",
1151*4882a593Smuzhiyun bss->SSID, MAC2STRDBG(bi->BSSID.octet),
1152*4882a593Smuzhiyun bss->RSSI, bi->RSSI));
1153*4882a593Smuzhiyun bi->RSSI = bss->RSSI;
1154*4882a593Smuzhiyun bi->flags |= WL_BSS_FLAGS_RSSI_ONCHANNEL;
1155*4882a593Smuzhiyun }
1156*4882a593Smuzhiyun if (dtoh32(bss->length) != bi_length) {
1157*4882a593Smuzhiyun u32 prev_len = dtoh32(bss->length);
1158*4882a593Smuzhiyun
1159*4882a593Smuzhiyun WL_DBG(("bss info replacement"
1160*4882a593Smuzhiyun " is occured(bcast:%d->probresp%d)\n",
1161*4882a593Smuzhiyun bss->ie_length, bi->ie_length));
1162*4882a593Smuzhiyun WL_DBG(("%s("MACDBG"), replacement!(%d -> %d)\n",
1163*4882a593Smuzhiyun bss->SSID, MAC2STRDBG(bi->BSSID.octet),
1164*4882a593Smuzhiyun prev_len, bi_length));
1165*4882a593Smuzhiyun
1166*4882a593Smuzhiyun if ((list->buflen - prev_len) + bi_length
1167*4882a593Smuzhiyun > ESCAN_BUF_SIZE) {
1168*4882a593Smuzhiyun WL_ERR(("Buffer is too small: keep the"
1169*4882a593Smuzhiyun " previous result of this AP\n"));
1170*4882a593Smuzhiyun /* Only update RSSI */
1171*4882a593Smuzhiyun bss->RSSI = bi->RSSI;
1172*4882a593Smuzhiyun bss->flags |= (bi->flags
1173*4882a593Smuzhiyun & WL_BSS_FLAGS_RSSI_ONCHANNEL);
1174*4882a593Smuzhiyun goto exit;
1175*4882a593Smuzhiyun }
1176*4882a593Smuzhiyun
1177*4882a593Smuzhiyun if (i < list->count - 1) {
1178*4882a593Smuzhiyun /* memory copy required by this case only */
1179*4882a593Smuzhiyun memmove((u8 *)bss + bi_length,
1180*4882a593Smuzhiyun (u8 *)bss + prev_len,
1181*4882a593Smuzhiyun list->buflen - cur_len - prev_len);
1182*4882a593Smuzhiyun }
1183*4882a593Smuzhiyun list->buflen -= prev_len;
1184*4882a593Smuzhiyun list->buflen += bi_length;
1185*4882a593Smuzhiyun }
1186*4882a593Smuzhiyun list->version = dtoh32(bi->version);
1187*4882a593Smuzhiyun /* In the above code under check
1188*4882a593Smuzhiyun * '(dtoh32(bss->length) != bi_length)'
1189*4882a593Smuzhiyun * buffer overflow is avoided. bi_length
1190*4882a593Smuzhiyun * is already accounted in list->buflen
1191*4882a593Smuzhiyun */
1192*4882a593Smuzhiyun if ((err = memcpy_s((u8 *)bss,
1193*4882a593Smuzhiyun (ESCAN_BUF_SIZE - (list->buflen - bi_length)),
1194*4882a593Smuzhiyun (u8 *)bi, bi_length)) != BCME_OK) {
1195*4882a593Smuzhiyun WL_ERR(("Failed to copy the recent bss_info."
1196*4882a593Smuzhiyun "err:%d recv_len:%d bi_len:%d\n", err,
1197*4882a593Smuzhiyun ESCAN_BUF_SIZE - (list->buflen - bi_length),
1198*4882a593Smuzhiyun bi_length));
1199*4882a593Smuzhiyun /* This scenario should never happen. If it happens,
1200*4882a593Smuzhiyun * set list->count to zero for recovery
1201*4882a593Smuzhiyun */
1202*4882a593Smuzhiyun list->count = 0;
1203*4882a593Smuzhiyun list->buflen = 0;
1204*4882a593Smuzhiyun ASSERT(0);
1205*4882a593Smuzhiyun }
1206*4882a593Smuzhiyun goto exit;
1207*4882a593Smuzhiyun }
1208*4882a593Smuzhiyun cur_len += dtoh32(bss->length);
1209*4882a593Smuzhiyun }
1210*4882a593Smuzhiyun if (bi_length > ESCAN_BUF_SIZE - list->buflen) {
1211*4882a593Smuzhiyun #ifdef ESCAN_BUF_OVERFLOW_MGMT
1212*4882a593Smuzhiyun wl_cfg80211_remove_lowRSSI_info(list, candidate, bi);
1213*4882a593Smuzhiyun if (bi_length > ESCAN_BUF_SIZE - list->buflen) {
1214*4882a593Smuzhiyun WL_DBG(("RSSI(" MACDBG ") is too low(%d) to add Buffer\n",
1215*4882a593Smuzhiyun MAC2STRDBG(bi->BSSID.octet), bi->RSSI));
1216*4882a593Smuzhiyun goto exit;
1217*4882a593Smuzhiyun }
1218*4882a593Smuzhiyun #else
1219*4882a593Smuzhiyun WL_ERR(("Buffer is too small: ignoring\n"));
1220*4882a593Smuzhiyun goto exit;
1221*4882a593Smuzhiyun #endif /* ESCAN_BUF_OVERFLOW_MGMT */
1222*4882a593Smuzhiyun }
1223*4882a593Smuzhiyun /* In the previous step check is added to ensure the bi_legth does not
1224*4882a593Smuzhiyun * exceed the ESCAN_BUF_SIZE
1225*4882a593Smuzhiyun */
1226*4882a593Smuzhiyun (void)memcpy_s(&(((char *)list)[list->buflen]),
1227*4882a593Smuzhiyun (ESCAN_BUF_SIZE - list->buflen), bi, bi_length);
1228*4882a593Smuzhiyun list->version = dtoh32(bi->version);
1229*4882a593Smuzhiyun list->buflen += bi_length;
1230*4882a593Smuzhiyun list->count++;
1231*4882a593Smuzhiyun
1232*4882a593Smuzhiyun /*
1233*4882a593Smuzhiyun * !Broadcast && number of ssid = 1 && number of channels =1
1234*4882a593Smuzhiyun * means specific scan to association
1235*4882a593Smuzhiyun */
1236*4882a593Smuzhiyun if (wl_cfgp2p_is_p2p_specific_scan(cfg->scan_request)) {
1237*4882a593Smuzhiyun WL_ERR(("P2P assoc scan fast aborted.\n"));
1238*4882a593Smuzhiyun wl_cfgscan_scan_abort(cfg);
1239*4882a593Smuzhiyun wl_notify_escan_complete(cfg, cfg->escan_info.ndev, false);
1240*4882a593Smuzhiyun goto exit;
1241*4882a593Smuzhiyun }
1242*4882a593Smuzhiyun }
1243*4882a593Smuzhiyun }
1244*4882a593Smuzhiyun else if (status == WLC_E_STATUS_SUCCESS) {
1245*4882a593Smuzhiyun cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
1246*4882a593Smuzhiyun #ifdef DHD_SEND_HANG_ESCAN_SYNCID_MISMATCH
1247*4882a593Smuzhiyun cfg->escan_info.prev_escan_aborted = FALSE;
1248*4882a593Smuzhiyun #endif /* DHD_SEND_HANG_ESCAN_SYNCID_MISMATCH */
1249*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, FINDING_COMMON_CHANNEL)) {
1250*4882a593Smuzhiyun WL_DBG(("ACTION FRAME SCAN DONE\n"));
1251*4882a593Smuzhiyun wl_clr_p2p_status(cfg, SCANNING);
1252*4882a593Smuzhiyun wl_clr_drv_status(cfg, SCANNING, cfg->afx_hdl->dev);
1253*4882a593Smuzhiyun if (cfg->afx_hdl->peer_chan == WL_INVALID)
1254*4882a593Smuzhiyun complete(&cfg->act_frm_scan);
1255*4882a593Smuzhiyun } else if ((likely(cfg->scan_request)) || (cfg->sched_scan_running)) {
1256*4882a593Smuzhiyun WL_INFORM_MEM(("ESCAN COMPLETED\n"));
1257*4882a593Smuzhiyun DBG_EVENT_LOG((dhd_pub_t *)cfg->pub, WIFI_EVENT_DRIVER_SCAN_COMPLETE);
1258*4882a593Smuzhiyun cfg->bss_list = wl_escan_get_buf(cfg, FALSE);
1259*4882a593Smuzhiyun if (!scan_req_match(cfg)) {
1260*4882a593Smuzhiyun WL_DBG(("SCAN COMPLETED: scanned AP count=%d\n",
1261*4882a593Smuzhiyun cfg->bss_list->count));
1262*4882a593Smuzhiyun }
1263*4882a593Smuzhiyun wl_inform_bss(cfg);
1264*4882a593Smuzhiyun wl_notify_escan_complete(cfg, ndev, false);
1265*4882a593Smuzhiyun }
1266*4882a593Smuzhiyun wl_escan_increment_sync_id(cfg, SCAN_BUF_NEXT);
1267*4882a593Smuzhiyun #ifdef CUSTOMER_HW4_DEBUG
1268*4882a593Smuzhiyun if (wl_scan_timeout_dbg_enabled)
1269*4882a593Smuzhiyun wl_scan_timeout_dbg_clear();
1270*4882a593Smuzhiyun #endif /* CUSTOMER_HW4_DEBUG */
1271*4882a593Smuzhiyun } else if ((status == WLC_E_STATUS_ABORT) || (status == WLC_E_STATUS_NEWSCAN) ||
1272*4882a593Smuzhiyun (status == WLC_E_STATUS_11HQUIET) || (status == WLC_E_STATUS_CS_ABORT) ||
1273*4882a593Smuzhiyun (status == WLC_E_STATUS_NEWASSOC)) {
1274*4882a593Smuzhiyun /* Dump FW preserve buffer content */
1275*4882a593Smuzhiyun if (status == WLC_E_STATUS_ABORT) {
1276*4882a593Smuzhiyun wl_flush_fw_log_buffer(ndev, FW_LOGSET_MASK_ALL);
1277*4882a593Smuzhiyun }
1278*4882a593Smuzhiyun /* Handle all cases of scan abort */
1279*4882a593Smuzhiyun cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
1280*4882a593Smuzhiyun WL_DBG(("ESCAN ABORT reason: %d\n", status));
1281*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, FINDING_COMMON_CHANNEL)) {
1282*4882a593Smuzhiyun WL_DBG(("ACTION FRAME SCAN DONE\n"));
1283*4882a593Smuzhiyun wl_clr_drv_status(cfg, SCANNING, cfg->afx_hdl->dev);
1284*4882a593Smuzhiyun wl_clr_p2p_status(cfg, SCANNING);
1285*4882a593Smuzhiyun if (cfg->afx_hdl->peer_chan == WL_INVALID)
1286*4882a593Smuzhiyun complete(&cfg->act_frm_scan);
1287*4882a593Smuzhiyun } else if ((likely(cfg->scan_request)) || (cfg->sched_scan_running)) {
1288*4882a593Smuzhiyun WL_INFORM_MEM(("ESCAN ABORTED\n"));
1289*4882a593Smuzhiyun
1290*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
1291*4882a593Smuzhiyun if (p2p_scan(cfg) && cfg->scan_request &&
1292*4882a593Smuzhiyun (cfg->scan_request->flags & NL80211_SCAN_FLAG_FLUSH)) {
1293*4882a593Smuzhiyun WL_ERR(("scan list is changed"));
1294*4882a593Smuzhiyun cfg->bss_list = wl_escan_get_buf(cfg, FALSE);
1295*4882a593Smuzhiyun } else
1296*4882a593Smuzhiyun #endif
1297*4882a593Smuzhiyun cfg->bss_list = wl_escan_get_buf(cfg, TRUE);
1298*4882a593Smuzhiyun
1299*4882a593Smuzhiyun if (!scan_req_match(cfg)) {
1300*4882a593Smuzhiyun WL_TRACE_HW4(("SCAN ABORTED: scanned AP count=%d\n",
1301*4882a593Smuzhiyun cfg->bss_list->count));
1302*4882a593Smuzhiyun }
1303*4882a593Smuzhiyun #ifdef DUAL_ESCAN_RESULT_BUFFER
1304*4882a593Smuzhiyun if (escan_result->sync_id != cfg->escan_info.cur_sync_id) {
1305*4882a593Smuzhiyun /* If sync_id is not matching, then the abort might have
1306*4882a593Smuzhiyun * come for the old scan req or for the in-driver initiated
1307*4882a593Smuzhiyun * scan. So do abort for scan_req for which sync_id is
1308*4882a593Smuzhiyun * matching.
1309*4882a593Smuzhiyun */
1310*4882a593Smuzhiyun WL_INFORM_MEM(("sync_id mismatch (%d != %d). "
1311*4882a593Smuzhiyun "Ignore the scan abort event.\n",
1312*4882a593Smuzhiyun escan_result->sync_id, cfg->escan_info.cur_sync_id));
1313*4882a593Smuzhiyun goto exit;
1314*4882a593Smuzhiyun } else {
1315*4882a593Smuzhiyun /* sync id is matching, abort the scan */
1316*4882a593Smuzhiyun WL_INFORM_MEM(("scan aborted for sync_id: %d \n",
1317*4882a593Smuzhiyun cfg->escan_info.cur_sync_id));
1318*4882a593Smuzhiyun wl_inform_bss(cfg);
1319*4882a593Smuzhiyun wl_notify_escan_complete(cfg, ndev, true);
1320*4882a593Smuzhiyun }
1321*4882a593Smuzhiyun #else
1322*4882a593Smuzhiyun wl_inform_bss(cfg);
1323*4882a593Smuzhiyun wl_notify_escan_complete(cfg, ndev, true);
1324*4882a593Smuzhiyun #endif /* DUAL_ESCAN_RESULT_BUFFER */
1325*4882a593Smuzhiyun } else {
1326*4882a593Smuzhiyun /* If there is no pending host initiated scan, do nothing */
1327*4882a593Smuzhiyun WL_DBG(("ESCAN ABORT: No pending scans. Ignoring event.\n"));
1328*4882a593Smuzhiyun }
1329*4882a593Smuzhiyun /* scan aborted, need to set previous success result */
1330*4882a593Smuzhiyun wl_escan_increment_sync_id(cfg, SCAN_BUF_CNT);
1331*4882a593Smuzhiyun } else if (status == WLC_E_STATUS_TIMEOUT) {
1332*4882a593Smuzhiyun WL_ERR(("WLC_E_STATUS_TIMEOUT : scan_request[%p]\n", cfg->scan_request));
1333*4882a593Smuzhiyun WL_ERR(("reason[0x%x]\n", e->reason));
1334*4882a593Smuzhiyun if (e->reason == 0xFFFFFFFF) {
1335*4882a593Smuzhiyun wl_scan_results_t *bss_list;
1336*4882a593Smuzhiyun bss_list = wl_escan_get_buf(cfg, FALSE);
1337*4882a593Smuzhiyun if (!bss_list) {
1338*4882a593Smuzhiyun WL_ERR(("bss_list is null. Didn't receive any partial scan results\n"));
1339*4882a593Smuzhiyun } else {
1340*4882a593Smuzhiyun WL_ERR(("Dump scan buffer: scanned AP count (%d)\n", bss_list->count));
1341*4882a593Smuzhiyun bi = NULL;
1342*4882a593Smuzhiyun bi = next_bss(bss_list, bi);
1343*4882a593Smuzhiyun for_each_bss(bss_list, bi, i) {
1344*4882a593Smuzhiyun channel = wf_chspec_ctlchan(wl_chspec_driver_to_host(bi->chanspec));
1345*4882a593Smuzhiyun WL_ERR(("SSID :%s Channel :%d\n", bi->SSID, channel));
1346*4882a593Smuzhiyun }
1347*4882a593Smuzhiyun }
1348*4882a593Smuzhiyun _wl_cfgscan_cancel_scan(cfg);
1349*4882a593Smuzhiyun }
1350*4882a593Smuzhiyun } else {
1351*4882a593Smuzhiyun WL_ERR(("unexpected Escan Event %d : abort\n", status));
1352*4882a593Smuzhiyun cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
1353*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, FINDING_COMMON_CHANNEL)) {
1354*4882a593Smuzhiyun WL_DBG(("ACTION FRAME SCAN DONE\n"));
1355*4882a593Smuzhiyun wl_clr_p2p_status(cfg, SCANNING);
1356*4882a593Smuzhiyun wl_clr_drv_status(cfg, SCANNING, cfg->afx_hdl->dev);
1357*4882a593Smuzhiyun if (cfg->afx_hdl->peer_chan == WL_INVALID)
1358*4882a593Smuzhiyun complete(&cfg->act_frm_scan);
1359*4882a593Smuzhiyun } else if ((likely(cfg->scan_request)) || (cfg->sched_scan_running)) {
1360*4882a593Smuzhiyun cfg->bss_list = wl_escan_get_buf(cfg, TRUE);
1361*4882a593Smuzhiyun if (!scan_req_match(cfg)) {
1362*4882a593Smuzhiyun WL_TRACE_HW4(("SCAN ABORTED(UNEXPECTED): "
1363*4882a593Smuzhiyun "scanned AP count=%d\n",
1364*4882a593Smuzhiyun cfg->bss_list->count));
1365*4882a593Smuzhiyun }
1366*4882a593Smuzhiyun wl_inform_bss(cfg);
1367*4882a593Smuzhiyun wl_notify_escan_complete(cfg, ndev, true);
1368*4882a593Smuzhiyun }
1369*4882a593Smuzhiyun /* scan aborted, need to set previous success result */
1370*4882a593Smuzhiyun wl_escan_increment_sync_id(cfg, 2);
1371*4882a593Smuzhiyun }
1372*4882a593Smuzhiyun #else /* WL_DRV_AVOID_SCANCACHE */
1373*4882a593Smuzhiyun err = wl_escan_without_scan_cache(cfg, escan_result, ndev, e, status);
1374*4882a593Smuzhiyun #endif /* WL_DRV_AVOID_SCANCACHE */
1375*4882a593Smuzhiyun exit:
1376*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
1377*4882a593Smuzhiyun return err;
1378*4882a593Smuzhiyun }
1379*4882a593Smuzhiyun
1380*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) && defined(SUPPORT_RANDOM_MAC_SCAN)
1381*4882a593Smuzhiyun static const u8 *
wl_retrieve_wps_attribute(const u8 * buf,u16 element_id)1382*4882a593Smuzhiyun wl_retrieve_wps_attribute(const u8 *buf, u16 element_id)
1383*4882a593Smuzhiyun {
1384*4882a593Smuzhiyun const wl_wps_ie_t *ie = NULL;
1385*4882a593Smuzhiyun u16 len = 0;
1386*4882a593Smuzhiyun const u8 *attrib;
1387*4882a593Smuzhiyun
1388*4882a593Smuzhiyun if (!buf) {
1389*4882a593Smuzhiyun WL_ERR(("WPS IE not present"));
1390*4882a593Smuzhiyun return 0;
1391*4882a593Smuzhiyun }
1392*4882a593Smuzhiyun
1393*4882a593Smuzhiyun ie = (const wl_wps_ie_t*) buf;
1394*4882a593Smuzhiyun len = ie->len;
1395*4882a593Smuzhiyun
1396*4882a593Smuzhiyun /* Point subel to the P2P IE's subelt field.
1397*4882a593Smuzhiyun * Subtract the preceding fields (id, len, OUI, oui_type) from the length.
1398*4882a593Smuzhiyun */
1399*4882a593Smuzhiyun attrib = ie->attrib;
1400*4882a593Smuzhiyun len -= 4; /* exclude OUI + OUI_TYPE */
1401*4882a593Smuzhiyun
1402*4882a593Smuzhiyun /* Search for attrib */
1403*4882a593Smuzhiyun return wl_find_attribute(attrib, len, element_id);
1404*4882a593Smuzhiyun }
1405*4882a593Smuzhiyun
1406*4882a593Smuzhiyun bool
wl_is_wps_enrollee_active(struct net_device * ndev,const u8 * ie_ptr,u16 len)1407*4882a593Smuzhiyun wl_is_wps_enrollee_active(struct net_device *ndev, const u8 *ie_ptr, u16 len)
1408*4882a593Smuzhiyun {
1409*4882a593Smuzhiyun const u8 *ie;
1410*4882a593Smuzhiyun const u8 *attrib;
1411*4882a593Smuzhiyun
1412*4882a593Smuzhiyun if ((ie = (const u8 *)wl_cfgp2p_find_wpsie(ie_ptr, len)) == NULL) {
1413*4882a593Smuzhiyun WL_DBG(("WPS IE not present. Do nothing.\n"));
1414*4882a593Smuzhiyun return false;
1415*4882a593Smuzhiyun }
1416*4882a593Smuzhiyun
1417*4882a593Smuzhiyun if ((attrib = wl_retrieve_wps_attribute(ie, WPS_ATTR_REQ_TYPE)) == NULL) {
1418*4882a593Smuzhiyun WL_DBG(("WPS_ATTR_REQ_TYPE not found!\n"));
1419*4882a593Smuzhiyun return false;
1420*4882a593Smuzhiyun }
1421*4882a593Smuzhiyun
1422*4882a593Smuzhiyun if (*attrib == WPS_REQ_TYPE_ENROLLEE) {
1423*4882a593Smuzhiyun WL_INFORM_MEM(("WPS Enrolle Active\n"));
1424*4882a593Smuzhiyun return true;
1425*4882a593Smuzhiyun } else {
1426*4882a593Smuzhiyun WL_DBG(("WPS_REQ_TYPE:%d\n", *attrib));
1427*4882a593Smuzhiyun }
1428*4882a593Smuzhiyun
1429*4882a593Smuzhiyun return false;
1430*4882a593Smuzhiyun }
1431*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) && defined(SUPPORT_RANDOM_MAC_SCAN) */
1432*4882a593Smuzhiyun
1433*4882a593Smuzhiyun /* Find listen channel */
wl_find_listen_channel(struct bcm_cfg80211 * cfg,const u8 * ie,u32 ie_len)1434*4882a593Smuzhiyun static s32 wl_find_listen_channel(struct bcm_cfg80211 *cfg,
1435*4882a593Smuzhiyun const u8 *ie, u32 ie_len)
1436*4882a593Smuzhiyun {
1437*4882a593Smuzhiyun const wifi_p2p_ie_t *p2p_ie;
1438*4882a593Smuzhiyun const u8 *end, *pos;
1439*4882a593Smuzhiyun s32 listen_channel;
1440*4882a593Smuzhiyun
1441*4882a593Smuzhiyun pos = (const u8 *)ie;
1442*4882a593Smuzhiyun
1443*4882a593Smuzhiyun p2p_ie = wl_cfgp2p_find_p2pie(pos, ie_len);
1444*4882a593Smuzhiyun
1445*4882a593Smuzhiyun if (p2p_ie == NULL) {
1446*4882a593Smuzhiyun return 0;
1447*4882a593Smuzhiyun }
1448*4882a593Smuzhiyun
1449*4882a593Smuzhiyun if (p2p_ie->len < MIN_P2P_IE_LEN || p2p_ie->len > MAX_P2P_IE_LEN) {
1450*4882a593Smuzhiyun CFGP2P_ERR(("p2p_ie->len out of range - %d\n", p2p_ie->len));
1451*4882a593Smuzhiyun return 0;
1452*4882a593Smuzhiyun }
1453*4882a593Smuzhiyun pos = p2p_ie->subelts;
1454*4882a593Smuzhiyun end = p2p_ie->subelts + (p2p_ie->len - 4);
1455*4882a593Smuzhiyun
1456*4882a593Smuzhiyun CFGP2P_DBG((" found p2p ie ! lenth %d \n",
1457*4882a593Smuzhiyun p2p_ie->len));
1458*4882a593Smuzhiyun
1459*4882a593Smuzhiyun while (pos < end) {
1460*4882a593Smuzhiyun uint16 attr_len;
1461*4882a593Smuzhiyun if (pos + 2 >= end) {
1462*4882a593Smuzhiyun CFGP2P_DBG((" -- Invalid P2P attribute"));
1463*4882a593Smuzhiyun return 0;
1464*4882a593Smuzhiyun }
1465*4882a593Smuzhiyun attr_len = ((uint16) (((pos + 1)[1] << 8) | (pos + 1)[0]));
1466*4882a593Smuzhiyun
1467*4882a593Smuzhiyun if (pos + 3 + attr_len > end) {
1468*4882a593Smuzhiyun CFGP2P_DBG(("P2P: Attribute underflow "
1469*4882a593Smuzhiyun "(len=%u left=%d)",
1470*4882a593Smuzhiyun attr_len, (int) (end - pos - 3)));
1471*4882a593Smuzhiyun return 0;
1472*4882a593Smuzhiyun }
1473*4882a593Smuzhiyun
1474*4882a593Smuzhiyun /* if Listen Channel att id is 6 and the vailue is valid,
1475*4882a593Smuzhiyun * return the listen channel
1476*4882a593Smuzhiyun */
1477*4882a593Smuzhiyun if (pos[0] == 6) {
1478*4882a593Smuzhiyun /* listen channel subel length format
1479*4882a593Smuzhiyun * 1(id) + 2(len) + 3(country) + 1(op. class) + 1(chan num)
1480*4882a593Smuzhiyun */
1481*4882a593Smuzhiyun listen_channel = pos[1 + 2 + 3 + 1];
1482*4882a593Smuzhiyun
1483*4882a593Smuzhiyun if (listen_channel == SOCIAL_CHAN_1 ||
1484*4882a593Smuzhiyun listen_channel == SOCIAL_CHAN_2 ||
1485*4882a593Smuzhiyun listen_channel == SOCIAL_CHAN_3) {
1486*4882a593Smuzhiyun CFGP2P_DBG((" Found my Listen Channel %d \n", listen_channel));
1487*4882a593Smuzhiyun return listen_channel;
1488*4882a593Smuzhiyun }
1489*4882a593Smuzhiyun }
1490*4882a593Smuzhiyun pos += 3 + attr_len;
1491*4882a593Smuzhiyun }
1492*4882a593Smuzhiyun return 0;
1493*4882a593Smuzhiyun }
1494*4882a593Smuzhiyun
1495*4882a593Smuzhiyun #ifdef WL_SCAN_TYPE
1496*4882a593Smuzhiyun static u32
wl_cfgscan_map_nl80211_scan_type(struct bcm_cfg80211 * cfg,struct cfg80211_scan_request * request)1497*4882a593Smuzhiyun wl_cfgscan_map_nl80211_scan_type(struct bcm_cfg80211 *cfg, struct cfg80211_scan_request *request)
1498*4882a593Smuzhiyun {
1499*4882a593Smuzhiyun u32 scan_flags = 0;
1500*4882a593Smuzhiyun
1501*4882a593Smuzhiyun if (!request) {
1502*4882a593Smuzhiyun return scan_flags;
1503*4882a593Smuzhiyun }
1504*4882a593Smuzhiyun
1505*4882a593Smuzhiyun if (request->flags & NL80211_SCAN_FLAG_LOW_SPAN) {
1506*4882a593Smuzhiyun scan_flags |= WL_SCANFLAGS_LOW_SPAN;
1507*4882a593Smuzhiyun }
1508*4882a593Smuzhiyun if (request->flags & NL80211_SCAN_FLAG_HIGH_ACCURACY) {
1509*4882a593Smuzhiyun scan_flags |= WL_SCANFLAGS_HIGH_ACCURACY;
1510*4882a593Smuzhiyun }
1511*4882a593Smuzhiyun if (request->flags & NL80211_SCAN_FLAG_LOW_POWER) {
1512*4882a593Smuzhiyun scan_flags |= WL_SCANFLAGS_LOW_POWER_SCAN;
1513*4882a593Smuzhiyun }
1514*4882a593Smuzhiyun if (request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) {
1515*4882a593Smuzhiyun scan_flags |= WL_SCANFLAGS_LOW_PRIO;
1516*4882a593Smuzhiyun }
1517*4882a593Smuzhiyun
1518*4882a593Smuzhiyun WL_INFORM(("scan flags. wl:%x cfg80211:%x\n", scan_flags, request->flags));
1519*4882a593Smuzhiyun return scan_flags;
1520*4882a593Smuzhiyun }
1521*4882a593Smuzhiyun #endif /* WL_SCAN_TYPE */
1522*4882a593Smuzhiyun
wl_freq_to_chanspec(int freq)1523*4882a593Smuzhiyun chanspec_t wl_freq_to_chanspec(int freq)
1524*4882a593Smuzhiyun {
1525*4882a593Smuzhiyun chanspec_t chanspec = 0;
1526*4882a593Smuzhiyun u16 bw;
1527*4882a593Smuzhiyun
1528*4882a593Smuzhiyun /* see 802.11 17.3.8.3.2 and Annex J */
1529*4882a593Smuzhiyun if (freq == 2484) {
1530*4882a593Smuzhiyun chanspec = 14;
1531*4882a593Smuzhiyun chanspec |= WL_CHANSPEC_BAND_2G;
1532*4882a593Smuzhiyun bw = WL_CHANSPEC_BW_20;
1533*4882a593Smuzhiyun } else if (freq >= 2412 && freq < 2484) {
1534*4882a593Smuzhiyun chanspec = (freq - 2407) / 5;
1535*4882a593Smuzhiyun chanspec |= WL_CHANSPEC_BAND_2G;
1536*4882a593Smuzhiyun bw = WL_CHANSPEC_BW_20;
1537*4882a593Smuzhiyun } else if (freq >= 4005 && freq <= 4980) {
1538*4882a593Smuzhiyun chanspec = (freq - 4000) / 5;
1539*4882a593Smuzhiyun chanspec |= WL_CHANSPEC_BAND_5G;
1540*4882a593Smuzhiyun bw = WL_CHANSPEC_BW_20;
1541*4882a593Smuzhiyun } else if (freq >= 5005 && freq < 5895) {
1542*4882a593Smuzhiyun chanspec = (freq - 5000) / 5;
1543*4882a593Smuzhiyun chanspec |= WL_CHANSPEC_BAND_5G;
1544*4882a593Smuzhiyun bw = WL_CHANSPEC_BW_20;
1545*4882a593Smuzhiyun #ifdef WL_6G_BAND
1546*4882a593Smuzhiyun } else if (freq >= 5945 && freq <= 7200) {
1547*4882a593Smuzhiyun /* see 802.11ax D4.1 27.3.22.2 */
1548*4882a593Smuzhiyun chanspec = (freq - 5950) / 5;
1549*4882a593Smuzhiyun bw = WL_CHANSPEC_BW_20;
1550*4882a593Smuzhiyun if ((chanspec % 8) == 3) {
1551*4882a593Smuzhiyun bw = WL_CHANSPEC_BW_40;
1552*4882a593Smuzhiyun } else if ((chanspec % 16) == 7) {
1553*4882a593Smuzhiyun bw = WL_CHANSPEC_BW_80;
1554*4882a593Smuzhiyun } else if ((chanspec % 32) == 15) {
1555*4882a593Smuzhiyun bw = WL_CHANSPEC_BW_160;
1556*4882a593Smuzhiyun }
1557*4882a593Smuzhiyun chanspec |= WL_CHANSPEC_BAND_6G;
1558*4882a593Smuzhiyun } else if (freq == 5935) {
1559*4882a593Smuzhiyun chanspec = 2;
1560*4882a593Smuzhiyun bw = WL_CHANSPEC_BW_20;
1561*4882a593Smuzhiyun chanspec |= WL_CHANSPEC_BAND_6G;
1562*4882a593Smuzhiyun #endif /* WL_6G_BAND */
1563*4882a593Smuzhiyun } else {
1564*4882a593Smuzhiyun WL_ERR(("Invalid frequency %d\n", freq));
1565*4882a593Smuzhiyun return INVCHANSPEC;
1566*4882a593Smuzhiyun }
1567*4882a593Smuzhiyun
1568*4882a593Smuzhiyun /* Get the min_bw set for the interface */
1569*4882a593Smuzhiyun chanspec |= bw;
1570*4882a593Smuzhiyun chanspec |= WL_CHANSPEC_CTL_SB_NONE;
1571*4882a593Smuzhiyun
1572*4882a593Smuzhiyun return chanspec;
1573*4882a593Smuzhiyun }
1574*4882a593Smuzhiyun
1575*4882a593Smuzhiyun #ifdef SCAN_SUPPRESS
1576*4882a593Smuzhiyun static void
wl_cfgscan_populate_scan_channel(struct bcm_cfg80211 * cfg,struct ieee80211_channel ** channels,u32 n_channels,u16 * channel_list,struct wl_chan_info * chan_info)1577*4882a593Smuzhiyun wl_cfgscan_populate_scan_channel(struct bcm_cfg80211 *cfg,
1578*4882a593Smuzhiyun struct ieee80211_channel **channels, u32 n_channels,
1579*4882a593Smuzhiyun u16 *channel_list, struct wl_chan_info *chan_info)
1580*4882a593Smuzhiyun {
1581*4882a593Smuzhiyun u32 i, chanspec = 0;
1582*4882a593Smuzhiyun
1583*4882a593Smuzhiyun for (i=0; i<n_channels; i++) {
1584*4882a593Smuzhiyun chanspec = wl_freq_to_chanspec(channels[i]->center_freq);
1585*4882a593Smuzhiyun if (chanspec == INVCHANSPEC) {
1586*4882a593Smuzhiyun WL_ERR(("Invalid chanspec! Skipping channel\n"));
1587*4882a593Smuzhiyun continue;
1588*4882a593Smuzhiyun }
1589*4882a593Smuzhiyun if (chan_info->band == CHSPEC2WLC_BAND(chanspec) &&
1590*4882a593Smuzhiyun chan_info->chan == wf_chspec_ctlchan(chanspec)) {
1591*4882a593Smuzhiyun channel_list[0] = chanspec;
1592*4882a593Smuzhiyun break;
1593*4882a593Smuzhiyun }
1594*4882a593Smuzhiyun }
1595*4882a593Smuzhiyun WL_SCAN(("chan: %s-%d, chanspec: %x\n",
1596*4882a593Smuzhiyun WLCBAND2STR(chan_info->band), chan_info->chan, chanspec));
1597*4882a593Smuzhiyun }
1598*4882a593Smuzhiyun #endif
1599*4882a593Smuzhiyun
1600*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
1601*4882a593Smuzhiyun #define IS_RADAR_CHAN(flags) (flags & (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_PASSIVE_SCAN))
1602*4882a593Smuzhiyun #else
1603*4882a593Smuzhiyun #define IS_RADAR_CHAN(flags) (flags & (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR))
1604*4882a593Smuzhiyun #endif
1605*4882a593Smuzhiyun static void
wl_cfgscan_populate_scan_channels(struct bcm_cfg80211 * cfg,struct ieee80211_channel ** channels,u32 n_channels,u16 * channel_list,u32 * num_channels,bool use_chanspecs,bool skip_dfs)1606*4882a593Smuzhiyun wl_cfgscan_populate_scan_channels(struct bcm_cfg80211 *cfg,
1607*4882a593Smuzhiyun struct ieee80211_channel **channels, u32 n_channels,
1608*4882a593Smuzhiyun u16 *channel_list, u32 *num_channels, bool use_chanspecs, bool skip_dfs)
1609*4882a593Smuzhiyun {
1610*4882a593Smuzhiyun u32 i = 0, j = 0;
1611*4882a593Smuzhiyun u32 chanspec = 0;
1612*4882a593Smuzhiyun struct wireless_dev *wdev;
1613*4882a593Smuzhiyun bool is_p2p_scan = false;
1614*4882a593Smuzhiyun #ifdef P2P_SKIP_DFS
1615*4882a593Smuzhiyun int is_printed = false;
1616*4882a593Smuzhiyun #endif /* P2P_SKIP_DFS */
1617*4882a593Smuzhiyun u32 channel;
1618*4882a593Smuzhiyun
1619*4882a593Smuzhiyun if (!channels || !n_channels) {
1620*4882a593Smuzhiyun /* Do full channel scan */
1621*4882a593Smuzhiyun return;
1622*4882a593Smuzhiyun }
1623*4882a593Smuzhiyun
1624*4882a593Smuzhiyun wdev = GET_SCAN_WDEV(cfg->scan_request);
1625*4882a593Smuzhiyun if (!skip_dfs && wdev && wdev->netdev &&
1626*4882a593Smuzhiyun (wdev->netdev != bcmcfg_to_prmry_ndev(cfg))) {
1627*4882a593Smuzhiyun /* SKIP DFS channels for Secondary interface */
1628*4882a593Smuzhiyun skip_dfs = true;
1629*4882a593Smuzhiyun }
1630*4882a593Smuzhiyun
1631*4882a593Smuzhiyun /* Check if request is for p2p scans */
1632*4882a593Smuzhiyun is_p2p_scan = p2p_is_on(cfg) && p2p_scan(cfg);
1633*4882a593Smuzhiyun
1634*4882a593Smuzhiyun for (i = 0; i < n_channels; i++) {
1635*4882a593Smuzhiyun channel = ieee80211_frequency_to_channel(channels[i]->center_freq);
1636*4882a593Smuzhiyun if (skip_dfs && (IS_RADAR_CHAN(channels[i]->flags))) {
1637*4882a593Smuzhiyun WL_DBG(("Skipping radar channel. freq:%d\n",
1638*4882a593Smuzhiyun (channels[i]->center_freq)));
1639*4882a593Smuzhiyun continue;
1640*4882a593Smuzhiyun }
1641*4882a593Smuzhiyun if (!dhd_conf_match_channel(cfg->pub, channel))
1642*4882a593Smuzhiyun continue;
1643*4882a593Smuzhiyun
1644*4882a593Smuzhiyun chanspec = wl_freq_to_chanspec(channels[i]->center_freq);
1645*4882a593Smuzhiyun if (chanspec == INVCHANSPEC) {
1646*4882a593Smuzhiyun WL_ERR(("Invalid chanspec! Skipping channel\n"));
1647*4882a593Smuzhiyun continue;
1648*4882a593Smuzhiyun }
1649*4882a593Smuzhiyun
1650*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
1651*4882a593Smuzhiyun if (channels[i]->band == IEEE80211_BAND_60GHZ) {
1652*4882a593Smuzhiyun /* Not supported */
1653*4882a593Smuzhiyun continue;
1654*4882a593Smuzhiyun }
1655*4882a593Smuzhiyun #endif /* LINUX_VER >= 3.6 */
1656*4882a593Smuzhiyun #ifdef WL_HOST_BAND_MGMT
1657*4882a593Smuzhiyun if (channels[i]->band == IEEE80211_BAND_2GHZ) {
1658*4882a593Smuzhiyun if ((cfg->curr_band == WLC_BAND_5G) ||
1659*4882a593Smuzhiyun (cfg->curr_band == WLC_BAND_6G)) {
1660*4882a593Smuzhiyun if !(is_p2p_scan &&
1661*4882a593Smuzhiyun IS_P2P_SOCIAL_CHANNEL(CHSPEC_CHANNEL(chanspec))) {
1662*4882a593Smuzhiyun WL_DBG(("In 5G only mode, omit 2G channel:%d\n", channel));
1663*4882a593Smuzhiyun continue;
1664*4882a593Smuzhiyun }
1665*4882a593Smuzhiyun }
1666*4882a593Smuzhiyun } else {
1667*4882a593Smuzhiyun if (cfg->curr_band == WLC_BAND_2G) {
1668*4882a593Smuzhiyun WL_DBG(("In 2G only mode, omit 5G channel:%d\n", channel));
1669*4882a593Smuzhiyun continue;
1670*4882a593Smuzhiyun }
1671*4882a593Smuzhiyun }
1672*4882a593Smuzhiyun #endif /* WL_HOST_BAND_MGMT */
1673*4882a593Smuzhiyun
1674*4882a593Smuzhiyun if (is_p2p_scan) {
1675*4882a593Smuzhiyun #ifdef WL_P2P_6G
1676*4882a593Smuzhiyun if (!(cfg->p2p_6g_enabled)) {
1677*4882a593Smuzhiyun #endif /* WL_P2P_6G */
1678*4882a593Smuzhiyun if (CHSPEC_IS6G(chanspec)) {
1679*4882a593Smuzhiyun continue;
1680*4882a593Smuzhiyun }
1681*4882a593Smuzhiyun #ifdef WL_P2P_6G
1682*4882a593Smuzhiyun }
1683*4882a593Smuzhiyun #endif /* WL_P2P_6G */
1684*4882a593Smuzhiyun
1685*4882a593Smuzhiyun #ifdef P2P_SKIP_DFS
1686*4882a593Smuzhiyun if (CHSPEC_IS5G(chanspec) &&
1687*4882a593Smuzhiyun (CHSPEC_CHANNEL(chanspec) >= 52 &&
1688*4882a593Smuzhiyun CHSPEC_CHANNEL(chanspec) <= 144)) {
1689*4882a593Smuzhiyun if (is_printed == false) {
1690*4882a593Smuzhiyun WL_ERR(("SKIP DFS CHANs(52~144)\n"));
1691*4882a593Smuzhiyun is_printed = true;
1692*4882a593Smuzhiyun }
1693*4882a593Smuzhiyun continue;
1694*4882a593Smuzhiyun }
1695*4882a593Smuzhiyun #endif /* P2P_SKIP_DFS */
1696*4882a593Smuzhiyun }
1697*4882a593Smuzhiyun
1698*4882a593Smuzhiyun if (use_chanspecs) {
1699*4882a593Smuzhiyun channel_list[j] = chanspec;
1700*4882a593Smuzhiyun } else {
1701*4882a593Smuzhiyun channel_list[j] = CHSPEC_CHANNEL(chanspec);
1702*4882a593Smuzhiyun }
1703*4882a593Smuzhiyun WL_SCAN(("chan: %d, chanspec: %x\n", channel, channel_list[j]));
1704*4882a593Smuzhiyun j++;
1705*4882a593Smuzhiyun if (j == WL_NUMCHANSPECS) {
1706*4882a593Smuzhiyun /* max limit */
1707*4882a593Smuzhiyun break;
1708*4882a593Smuzhiyun }
1709*4882a593Smuzhiyun }
1710*4882a593Smuzhiyun *num_channels = j;
1711*4882a593Smuzhiyun }
1712*4882a593Smuzhiyun
1713*4882a593Smuzhiyun static void
wl_cfgscan_populate_scan_ssids(struct bcm_cfg80211 * cfg,u8 * buf_ptr,u32 buf_len,struct cfg80211_scan_request * request,u32 * ssid_num)1714*4882a593Smuzhiyun wl_cfgscan_populate_scan_ssids(struct bcm_cfg80211 *cfg, u8 *buf_ptr, u32 buf_len,
1715*4882a593Smuzhiyun struct cfg80211_scan_request *request, u32 *ssid_num)
1716*4882a593Smuzhiyun {
1717*4882a593Smuzhiyun u32 n_ssids;
1718*4882a593Smuzhiyun wlc_ssid_t ssid;
1719*4882a593Smuzhiyun int i, j = 0;
1720*4882a593Smuzhiyun
1721*4882a593Smuzhiyun if (!request || !buf_ptr) {
1722*4882a593Smuzhiyun /* Do full channel scan */
1723*4882a593Smuzhiyun return;
1724*4882a593Smuzhiyun }
1725*4882a593Smuzhiyun
1726*4882a593Smuzhiyun n_ssids = request->n_ssids;
1727*4882a593Smuzhiyun if (n_ssids > 0) {
1728*4882a593Smuzhiyun
1729*4882a593Smuzhiyun if (buf_len < (n_ssids * sizeof(wlc_ssid_t))) {
1730*4882a593Smuzhiyun WL_ERR(("buf len not sufficient for scan ssids\n"));
1731*4882a593Smuzhiyun return;
1732*4882a593Smuzhiyun }
1733*4882a593Smuzhiyun
1734*4882a593Smuzhiyun for (i = 0; i < n_ssids; i++) {
1735*4882a593Smuzhiyun bzero(&ssid, sizeof(wlc_ssid_t));
1736*4882a593Smuzhiyun ssid.SSID_len = MIN(request->ssids[i].ssid_len, DOT11_MAX_SSID_LEN);
1737*4882a593Smuzhiyun /* Returning void here, as per previous line copy length does not exceed
1738*4882a593Smuzhiyun * DOT11_MAX_SSID_LEN
1739*4882a593Smuzhiyun */
1740*4882a593Smuzhiyun (void)memcpy_s(ssid.SSID, DOT11_MAX_SSID_LEN, request->ssids[i].ssid,
1741*4882a593Smuzhiyun ssid.SSID_len);
1742*4882a593Smuzhiyun if (!ssid.SSID_len) {
1743*4882a593Smuzhiyun WL_SCAN(("%d: Broadcast scan\n", i));
1744*4882a593Smuzhiyun } else {
1745*4882a593Smuzhiyun WL_SCAN(("%d: scan for %s size =%d\n", i,
1746*4882a593Smuzhiyun ssid.SSID, ssid.SSID_len));
1747*4882a593Smuzhiyun }
1748*4882a593Smuzhiyun /* For multiple ssid case copy the each SSID info the ptr below corresponds
1749*4882a593Smuzhiyun * to that so dest is of type wlc_ssid_t
1750*4882a593Smuzhiyun */
1751*4882a593Smuzhiyun (void)memcpy_s(buf_ptr, sizeof(wlc_ssid_t), &ssid, sizeof(wlc_ssid_t));
1752*4882a593Smuzhiyun buf_ptr += sizeof(wlc_ssid_t);
1753*4882a593Smuzhiyun j++;
1754*4882a593Smuzhiyun }
1755*4882a593Smuzhiyun } else {
1756*4882a593Smuzhiyun WL_SCAN(("Broadcast scan\n"));
1757*4882a593Smuzhiyun }
1758*4882a593Smuzhiyun *ssid_num = j;
1759*4882a593Smuzhiyun }
1760*4882a593Smuzhiyun
1761*4882a593Smuzhiyun static s32
wl_scan_prep(struct bcm_cfg80211 * cfg,struct net_device * ndev,void * scan_params,u32 len,struct cfg80211_scan_request * request)1762*4882a593Smuzhiyun wl_scan_prep(struct bcm_cfg80211 *cfg, struct net_device *ndev, void *scan_params, u32 len,
1763*4882a593Smuzhiyun struct cfg80211_scan_request *request)
1764*4882a593Smuzhiyun {
1765*4882a593Smuzhiyun #ifdef SCAN_SUPPRESS
1766*4882a593Smuzhiyun struct wl_chan_info chan_info;
1767*4882a593Smuzhiyun u32 channel;
1768*4882a593Smuzhiyun #endif
1769*4882a593Smuzhiyun wl_scan_params_t *params = NULL;
1770*4882a593Smuzhiyun wl_scan_params_v2_t *params_v2 = NULL;
1771*4882a593Smuzhiyun u32 scan_type = 0;
1772*4882a593Smuzhiyun u32 scan_param_size = 0;
1773*4882a593Smuzhiyun u32 n_channels = 0;
1774*4882a593Smuzhiyun u32 n_ssids = 0;
1775*4882a593Smuzhiyun uint16 *chan_list = NULL;
1776*4882a593Smuzhiyun u32 channel_offset = 0;
1777*4882a593Smuzhiyun u32 cur_offset;
1778*4882a593Smuzhiyun
1779*4882a593Smuzhiyun if (!scan_params) {
1780*4882a593Smuzhiyun return BCME_ERROR;
1781*4882a593Smuzhiyun }
1782*4882a593Smuzhiyun
1783*4882a593Smuzhiyun if (cfg->active_scan == PASSIVE_SCAN) {
1784*4882a593Smuzhiyun WL_INFORM_MEM(("Enforcing passive scan\n"));
1785*4882a593Smuzhiyun scan_type = WL_SCANFLAGS_PASSIVE;
1786*4882a593Smuzhiyun }
1787*4882a593Smuzhiyun
1788*4882a593Smuzhiyun WL_DBG(("Preparing Scan request\n"));
1789*4882a593Smuzhiyun if (cfg->scan_params_v2) {
1790*4882a593Smuzhiyun params_v2 = (wl_scan_params_v2_t *)scan_params;
1791*4882a593Smuzhiyun scan_param_size = sizeof(wl_scan_params_v2_t);
1792*4882a593Smuzhiyun channel_offset = offsetof(wl_scan_params_v2_t, channel_list);
1793*4882a593Smuzhiyun } else {
1794*4882a593Smuzhiyun params = (wl_scan_params_t *)scan_params;
1795*4882a593Smuzhiyun scan_param_size = sizeof(wl_scan_params_t);
1796*4882a593Smuzhiyun channel_offset = offsetof(wl_scan_params_t, channel_list);
1797*4882a593Smuzhiyun }
1798*4882a593Smuzhiyun
1799*4882a593Smuzhiyun if (params_v2) {
1800*4882a593Smuzhiyun /* scan params ver2 */
1801*4882a593Smuzhiyun #if defined(WL_SCAN_TYPE)
1802*4882a593Smuzhiyun scan_type += wl_cfgscan_map_nl80211_scan_type(cfg, request);
1803*4882a593Smuzhiyun #endif /* WL_SCAN_TYPE */
1804*4882a593Smuzhiyun
1805*4882a593Smuzhiyun (void)memcpy_s(¶ms_v2->bssid, ETHER_ADDR_LEN, ðer_bcast, ETHER_ADDR_LEN);
1806*4882a593Smuzhiyun params_v2->version = htod16(WL_SCAN_PARAMS_VERSION_V2);
1807*4882a593Smuzhiyun params_v2->length = htod16(sizeof(wl_scan_params_v2_t));
1808*4882a593Smuzhiyun params_v2->bss_type = DOT11_BSSTYPE_ANY;
1809*4882a593Smuzhiyun params_v2->scan_type = htod32(scan_type);
1810*4882a593Smuzhiyun params_v2->nprobes = htod32(-1);
1811*4882a593Smuzhiyun params_v2->active_time = htod32(-1);
1812*4882a593Smuzhiyun params_v2->passive_time = htod32(-1);
1813*4882a593Smuzhiyun params_v2->home_time = htod32(-1);
1814*4882a593Smuzhiyun params_v2->channel_num = 0;
1815*4882a593Smuzhiyun bzero(¶ms_v2->ssid, sizeof(wlc_ssid_t));
1816*4882a593Smuzhiyun chan_list = params_v2->channel_list;
1817*4882a593Smuzhiyun } else {
1818*4882a593Smuzhiyun /* scan params ver 1 */
1819*4882a593Smuzhiyun if (!params) {
1820*4882a593Smuzhiyun ASSERT(0);
1821*4882a593Smuzhiyun return BCME_ERROR;
1822*4882a593Smuzhiyun }
1823*4882a593Smuzhiyun (void)memcpy_s(¶ms->bssid, ETHER_ADDR_LEN, ðer_bcast, ETHER_ADDR_LEN);
1824*4882a593Smuzhiyun params->bss_type = DOT11_BSSTYPE_ANY;
1825*4882a593Smuzhiyun params->scan_type = 0;
1826*4882a593Smuzhiyun params->nprobes = htod32(-1);
1827*4882a593Smuzhiyun params->active_time = htod32(-1);
1828*4882a593Smuzhiyun params->passive_time = htod32(-1);
1829*4882a593Smuzhiyun params->home_time = htod32(-1);
1830*4882a593Smuzhiyun params->channel_num = 0;
1831*4882a593Smuzhiyun bzero(¶ms->ssid, sizeof(wlc_ssid_t));
1832*4882a593Smuzhiyun chan_list = params->channel_list;
1833*4882a593Smuzhiyun }
1834*4882a593Smuzhiyun
1835*4882a593Smuzhiyun if (!request) {
1836*4882a593Smuzhiyun /* scan_request null, do scan based on base config */
1837*4882a593Smuzhiyun WL_DBG(("scan_request is null\n"));
1838*4882a593Smuzhiyun return BCME_OK;
1839*4882a593Smuzhiyun }
1840*4882a593Smuzhiyun
1841*4882a593Smuzhiyun WL_INFORM(("n_channels:%d n_ssids:%d\n", request->n_channels, request->n_ssids));
1842*4882a593Smuzhiyun
1843*4882a593Smuzhiyun cur_offset = channel_offset;
1844*4882a593Smuzhiyun /* Copy channel array if applicable */
1845*4882a593Smuzhiyun #ifdef SCAN_SUPPRESS
1846*4882a593Smuzhiyun channel = wl_ext_scan_suppress(ndev, scan_params, cfg->scan_params_v2, &chan_info);
1847*4882a593Smuzhiyun if (channel) {
1848*4882a593Smuzhiyun n_channels = 1;
1849*4882a593Smuzhiyun if ((n_channels > 0) && chan_list) {
1850*4882a593Smuzhiyun if (len >= (scan_param_size + (n_channels * sizeof(u16)))) {
1851*4882a593Smuzhiyun wl_cfgscan_populate_scan_channel(cfg,
1852*4882a593Smuzhiyun request->channels, request->n_channels,
1853*4882a593Smuzhiyun chan_list, &chan_info);
1854*4882a593Smuzhiyun cur_offset += (n_channels * (sizeof(u16)));
1855*4882a593Smuzhiyun }
1856*4882a593Smuzhiyun }
1857*4882a593Smuzhiyun } else
1858*4882a593Smuzhiyun #endif
1859*4882a593Smuzhiyun if ((request->n_channels > 0) && chan_list) {
1860*4882a593Smuzhiyun if (len >= (scan_param_size + (request->n_channels * sizeof(u16)))) {
1861*4882a593Smuzhiyun wl_cfgscan_populate_scan_channels(cfg,
1862*4882a593Smuzhiyun request->channels, request->n_channels,
1863*4882a593Smuzhiyun chan_list, &n_channels, true, false);
1864*4882a593Smuzhiyun cur_offset += (uint32)(n_channels * (sizeof(u16)));
1865*4882a593Smuzhiyun }
1866*4882a593Smuzhiyun }
1867*4882a593Smuzhiyun
1868*4882a593Smuzhiyun /* Copy ssid array if applicable */
1869*4882a593Smuzhiyun if (request->n_ssids > 0) {
1870*4882a593Smuzhiyun cur_offset = (u32) roundup(cur_offset, sizeof(u32));
1871*4882a593Smuzhiyun if (len > (cur_offset + (request->n_ssids * sizeof(wlc_ssid_t)))) {
1872*4882a593Smuzhiyun u32 rem_len = len - cur_offset;
1873*4882a593Smuzhiyun wl_cfgscan_populate_scan_ssids(cfg,
1874*4882a593Smuzhiyun ((u8 *)scan_params + cur_offset), rem_len, request, &n_ssids);
1875*4882a593Smuzhiyun }
1876*4882a593Smuzhiyun }
1877*4882a593Smuzhiyun
1878*4882a593Smuzhiyun if (n_ssids || n_channels) {
1879*4882a593Smuzhiyun u32 channel_num =
1880*4882a593Smuzhiyun htod32((n_ssids << WL_SCAN_PARAMS_NSSID_SHIFT) |
1881*4882a593Smuzhiyun (n_channels & WL_SCAN_PARAMS_COUNT_MASK));
1882*4882a593Smuzhiyun if (params_v2) {
1883*4882a593Smuzhiyun params_v2->channel_num = channel_num;
1884*4882a593Smuzhiyun if (n_channels == 1) {
1885*4882a593Smuzhiyun params_v2->active_time = htod32(WL_SCAN_CONNECT_DWELL_TIME_MS);
1886*4882a593Smuzhiyun params_v2->nprobes = htod32(
1887*4882a593Smuzhiyun params_v2->active_time / WL_SCAN_JOIN_PROBE_INTERVAL_MS);
1888*4882a593Smuzhiyun }
1889*4882a593Smuzhiyun } else {
1890*4882a593Smuzhiyun params->channel_num = channel_num;
1891*4882a593Smuzhiyun if (n_channels == 1) {
1892*4882a593Smuzhiyun params->active_time = htod32(WL_SCAN_CONNECT_DWELL_TIME_MS);
1893*4882a593Smuzhiyun params->nprobes = htod32(
1894*4882a593Smuzhiyun params->active_time / WL_SCAN_JOIN_PROBE_INTERVAL_MS);
1895*4882a593Smuzhiyun }
1896*4882a593Smuzhiyun }
1897*4882a593Smuzhiyun }
1898*4882a593Smuzhiyun
1899*4882a593Smuzhiyun WL_DBG_MEM(("scan_prep done. n_channels:%d n_ssids:%d\n", n_channels, n_ssids));
1900*4882a593Smuzhiyun return BCME_OK;
1901*4882a593Smuzhiyun }
1902*4882a593Smuzhiyun
1903*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) && defined(SUPPORT_RANDOM_MAC_SCAN)
1904*4882a593Smuzhiyun static s32
wl_config_scan_macaddr(struct bcm_cfg80211 * cfg,struct net_device * ndev,bool randmac_enable,u8 * mac_addr,u8 * mac_addr_mask)1905*4882a593Smuzhiyun wl_config_scan_macaddr(struct bcm_cfg80211 *cfg,
1906*4882a593Smuzhiyun struct net_device *ndev, bool randmac_enable, u8 *mac_addr, u8 *mac_addr_mask)
1907*4882a593Smuzhiyun {
1908*4882a593Smuzhiyun s32 err = BCME_OK;
1909*4882a593Smuzhiyun
1910*4882a593Smuzhiyun if (randmac_enable) {
1911*4882a593Smuzhiyun if (!cfg->scanmac_enabled) {
1912*4882a593Smuzhiyun err = wl_cfg80211_scan_mac_enable(ndev);
1913*4882a593Smuzhiyun if (unlikely(err)) {
1914*4882a593Smuzhiyun goto exit;
1915*4882a593Smuzhiyun }
1916*4882a593Smuzhiyun WL_DBG(("randmac enabled\n"));
1917*4882a593Smuzhiyun }
1918*4882a593Smuzhiyun
1919*4882a593Smuzhiyun #ifdef WL_HOST_RANDMAC_CONFIG
1920*4882a593Smuzhiyun /* If mask provided, apply user space configuration */
1921*4882a593Smuzhiyun if (!mac_addr_mask && !mac_addr && !ETHER_ISNULLADDR(mac_addr_mask)) {
1922*4882a593Smuzhiyun err = wl_cfg80211_scan_mac_config(ndev,
1923*4882a593Smuzhiyun mac_addr, mac_addr_mask);
1924*4882a593Smuzhiyun if (unlikely(err)) {
1925*4882a593Smuzhiyun WL_ERR(("scan mac config failed\n"));
1926*4882a593Smuzhiyun goto exit;
1927*4882a593Smuzhiyun }
1928*4882a593Smuzhiyun }
1929*4882a593Smuzhiyun #endif /* WL_HOST_RANDMAC_CONFIG */
1930*4882a593Smuzhiyun if (cfg->scanmac_config) {
1931*4882a593Smuzhiyun /* Use default scanmac configuration */
1932*4882a593Smuzhiyun WL_DBG(("Use host provided scanmac config\n"));
1933*4882a593Smuzhiyun } else {
1934*4882a593Smuzhiyun WL_DBG(("Use fw default scanmac config\n"));
1935*4882a593Smuzhiyun }
1936*4882a593Smuzhiyun } else if (!randmac_enable && cfg->scanmac_enabled) {
1937*4882a593Smuzhiyun WL_DBG(("randmac disabled\n"));
1938*4882a593Smuzhiyun err = wl_cfg80211_scan_mac_disable(ndev);
1939*4882a593Smuzhiyun } else {
1940*4882a593Smuzhiyun WL_DBG(("no change in randmac configuration\n"));
1941*4882a593Smuzhiyun }
1942*4882a593Smuzhiyun
1943*4882a593Smuzhiyun exit:
1944*4882a593Smuzhiyun if (err < 0) {
1945*4882a593Smuzhiyun if (err == BCME_UNSUPPORTED) {
1946*4882a593Smuzhiyun /* Ignore if chip doesnt support the feature */
1947*4882a593Smuzhiyun err = BCME_OK;
1948*4882a593Smuzhiyun } else {
1949*4882a593Smuzhiyun /* For errors other than unsupported fail the scan */
1950*4882a593Smuzhiyun WL_ERR(("%s : failed to configure random mac for host scan, %d\n",
1951*4882a593Smuzhiyun __FUNCTION__, err));
1952*4882a593Smuzhiyun err = -EAGAIN;
1953*4882a593Smuzhiyun }
1954*4882a593Smuzhiyun }
1955*4882a593Smuzhiyun
1956*4882a593Smuzhiyun return err;
1957*4882a593Smuzhiyun }
1958*4882a593Smuzhiyun #endif /* LINUX VER > 3.19 && SUPPORT_RANDOM_MAC_SCAN */
1959*4882a593Smuzhiyun
1960*4882a593Smuzhiyun static s32
wl_run_escan(struct bcm_cfg80211 * cfg,struct net_device * ndev,struct cfg80211_scan_request * request,uint16 action)1961*4882a593Smuzhiyun wl_run_escan(struct bcm_cfg80211 *cfg, struct net_device *ndev,
1962*4882a593Smuzhiyun struct cfg80211_scan_request *request, uint16 action)
1963*4882a593Smuzhiyun {
1964*4882a593Smuzhiyun s32 err = BCME_OK;
1965*4882a593Smuzhiyun u32 num_chans = 0;
1966*4882a593Smuzhiyun u32 n_channels = 0;
1967*4882a593Smuzhiyun u32 n_ssids;
1968*4882a593Smuzhiyun s32 params_size;
1969*4882a593Smuzhiyun wl_escan_params_t *eparams = NULL;
1970*4882a593Smuzhiyun wl_escan_params_v2_t *eparams_v2 = NULL;
1971*4882a593Smuzhiyun u8 *scan_params = NULL;
1972*4882a593Smuzhiyun u8 *params = NULL;
1973*4882a593Smuzhiyun s32 search_state = WL_P2P_DISC_ST_SCAN;
1974*4882a593Smuzhiyun u16 *default_chan_list = NULL;
1975*4882a593Smuzhiyun s32 bssidx = -1;
1976*4882a593Smuzhiyun struct net_device *dev = NULL;
1977*4882a593Smuzhiyun #if defined(USE_INITIAL_2G_SCAN) || defined(USE_INITIAL_SHORT_DWELL_TIME)
1978*4882a593Smuzhiyun bool is_first_init_2g_scan = false;
1979*4882a593Smuzhiyun #endif /* USE_INITIAL_2G_SCAN || USE_INITIAL_SHORT_DWELL_TIME */
1980*4882a593Smuzhiyun p2p_scan_purpose_t p2p_scan_purpose = P2P_SCAN_PURPOSE_MIN;
1981*4882a593Smuzhiyun u32 chan_mem = 0;
1982*4882a593Smuzhiyun u32 sync_id = 0;
1983*4882a593Smuzhiyun
1984*4882a593Smuzhiyun WL_DBG(("Enter \n"));
1985*4882a593Smuzhiyun
1986*4882a593Smuzhiyun if (!cfg || !request) {
1987*4882a593Smuzhiyun err = -EINVAL;
1988*4882a593Smuzhiyun goto exit;
1989*4882a593Smuzhiyun }
1990*4882a593Smuzhiyun
1991*4882a593Smuzhiyun if (cfg->scan_params_v2) {
1992*4882a593Smuzhiyun params_size = (WL_SCAN_PARAMS_V2_FIXED_SIZE +
1993*4882a593Smuzhiyun OFFSETOF(wl_escan_params_v2_t, params));
1994*4882a593Smuzhiyun } else {
1995*4882a593Smuzhiyun params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params));
1996*4882a593Smuzhiyun }
1997*4882a593Smuzhiyun
1998*4882a593Smuzhiyun if (!cfg->p2p_supported || !p2p_scan(cfg)) {
1999*4882a593Smuzhiyun /* LEGACY SCAN TRIGGER */
2000*4882a593Smuzhiyun WL_SCAN((" LEGACY E-SCAN START\n"));
2001*4882a593Smuzhiyun
2002*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) && defined(SUPPORT_RANDOM_MAC_SCAN)
2003*4882a593Smuzhiyun if (request) {
2004*4882a593Smuzhiyun bool randmac_enable = (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR);
2005*4882a593Smuzhiyun if (wl_is_wps_enrollee_active(ndev, request->ie, request->ie_len)) {
2006*4882a593Smuzhiyun randmac_enable = false;
2007*4882a593Smuzhiyun }
2008*4882a593Smuzhiyun if ((err = wl_config_scan_macaddr(cfg, ndev, randmac_enable,
2009*4882a593Smuzhiyun request->mac_addr, request->mac_addr_mask)) != BCME_OK) {
2010*4882a593Smuzhiyun WL_ERR(("scanmac addr config failed\n"));
2011*4882a593Smuzhiyun goto exit;
2012*4882a593Smuzhiyun }
2013*4882a593Smuzhiyun }
2014*4882a593Smuzhiyun #endif /* KERNEL_VER >= 3.19 && SUPPORT_RANDOM_MAC_SCAN */
2015*4882a593Smuzhiyun
2016*4882a593Smuzhiyun #if defined(USE_INITIAL_2G_SCAN) || defined(USE_INITIAL_SHORT_DWELL_TIME)
2017*4882a593Smuzhiyun if (ndev == bcmcfg_to_prmry_ndev(cfg) && g_first_broadcast_scan == true) {
2018*4882a593Smuzhiyun #ifdef USE_INITIAL_2G_SCAN
2019*4882a593Smuzhiyun struct ieee80211_channel tmp_channel_list[CH_MAX_2G_CHANNEL];
2020*4882a593Smuzhiyun /* allow one 5G channel to add previous connected channel in 5G */
2021*4882a593Smuzhiyun bool allow_one_5g_channel = TRUE;
2022*4882a593Smuzhiyun int i, j;
2023*4882a593Smuzhiyun j = 0;
2024*4882a593Smuzhiyun for (i = 0; i < request->n_channels; i++) {
2025*4882a593Smuzhiyun int tmp_chan = ieee80211_frequency_to_channel
2026*4882a593Smuzhiyun (request->channels[i]->center_freq);
2027*4882a593Smuzhiyun if (tmp_chan > CH_MAX_2G_CHANNEL) {
2028*4882a593Smuzhiyun if (allow_one_5g_channel)
2029*4882a593Smuzhiyun allow_one_5g_channel = FALSE;
2030*4882a593Smuzhiyun else
2031*4882a593Smuzhiyun continue;
2032*4882a593Smuzhiyun }
2033*4882a593Smuzhiyun if (j > CH_MAX_2G_CHANNEL) {
2034*4882a593Smuzhiyun WL_ERR(("Index %d exceeds max 2.4GHz channels %d"
2035*4882a593Smuzhiyun " and previous 5G connected channel\n",
2036*4882a593Smuzhiyun j, CH_MAX_2G_CHANNEL));
2037*4882a593Smuzhiyun break;
2038*4882a593Smuzhiyun }
2039*4882a593Smuzhiyun bcopy(request->channels[i], &tmp_channel_list[j],
2040*4882a593Smuzhiyun sizeof(struct ieee80211_channel));
2041*4882a593Smuzhiyun WL_SCAN(("channel of request->channels[%d]=%d\n", i, tmp_chan));
2042*4882a593Smuzhiyun j++;
2043*4882a593Smuzhiyun }
2044*4882a593Smuzhiyun if ((j > 0) && (j <= CH_MAX_2G_CHANNEL)) {
2045*4882a593Smuzhiyun for (i = 0; i < j; i++)
2046*4882a593Smuzhiyun bcopy(&tmp_channel_list[i], request->channels[i],
2047*4882a593Smuzhiyun sizeof(struct ieee80211_channel));
2048*4882a593Smuzhiyun
2049*4882a593Smuzhiyun request->n_channels = j;
2050*4882a593Smuzhiyun is_first_init_2g_scan = true;
2051*4882a593Smuzhiyun }
2052*4882a593Smuzhiyun else
2053*4882a593Smuzhiyun WL_ERR(("Invalid number of 2.4GHz channels %d\n", j));
2054*4882a593Smuzhiyun
2055*4882a593Smuzhiyun WL_SCAN(("request->n_channels=%d\n", request->n_channels));
2056*4882a593Smuzhiyun #else /* USE_INITIAL_SHORT_DWELL_TIME */
2057*4882a593Smuzhiyun is_first_init_2g_scan = true;
2058*4882a593Smuzhiyun #endif /* USE_INITIAL_2G_SCAN */
2059*4882a593Smuzhiyun g_first_broadcast_scan = false;
2060*4882a593Smuzhiyun }
2061*4882a593Smuzhiyun #endif /* USE_INITIAL_2G_SCAN || USE_INITIAL_SHORT_DWELL_TIME */
2062*4882a593Smuzhiyun
2063*4882a593Smuzhiyun n_channels = request->n_channels;
2064*4882a593Smuzhiyun n_ssids = request->n_ssids;
2065*4882a593Smuzhiyun if (n_channels % 2)
2066*4882a593Smuzhiyun /* If n_channels is odd, add a padd of u16 */
2067*4882a593Smuzhiyun params_size += sizeof(u16) * (n_channels + 1);
2068*4882a593Smuzhiyun else
2069*4882a593Smuzhiyun params_size += sizeof(u16) * n_channels;
2070*4882a593Smuzhiyun
2071*4882a593Smuzhiyun /* Allocate space for populating ssids in wl_escan_params_t struct */
2072*4882a593Smuzhiyun params_size += sizeof(struct wlc_ssid) * n_ssids;
2073*4882a593Smuzhiyun params = MALLOCZ(cfg->osh, params_size);
2074*4882a593Smuzhiyun if (params == NULL) {
2075*4882a593Smuzhiyun err = -ENOMEM;
2076*4882a593Smuzhiyun goto exit;
2077*4882a593Smuzhiyun }
2078*4882a593Smuzhiyun
2079*4882a593Smuzhiyun wl_escan_set_sync_id(sync_id, cfg);
2080*4882a593Smuzhiyun if (cfg->scan_params_v2) {
2081*4882a593Smuzhiyun eparams_v2 = (wl_escan_params_v2_t *)params;
2082*4882a593Smuzhiyun scan_params = (u8 *)&eparams_v2->params;
2083*4882a593Smuzhiyun eparams_v2->version = htod32(ESCAN_REQ_VERSION_V2);
2084*4882a593Smuzhiyun eparams_v2->action = htod16(action);
2085*4882a593Smuzhiyun eparams_v2->sync_id = sync_id;
2086*4882a593Smuzhiyun } else {
2087*4882a593Smuzhiyun eparams = (wl_escan_params_t *)params;
2088*4882a593Smuzhiyun scan_params = (u8 *)&eparams->params;
2089*4882a593Smuzhiyun eparams->version = htod32(ESCAN_REQ_VERSION);
2090*4882a593Smuzhiyun eparams->action = htod16(action);
2091*4882a593Smuzhiyun eparams->sync_id = sync_id;
2092*4882a593Smuzhiyun }
2093*4882a593Smuzhiyun
2094*4882a593Smuzhiyun if (wl_scan_prep(cfg, ndev, scan_params, params_size, request) < 0) {
2095*4882a593Smuzhiyun WL_ERR(("scan_prep failed\n"));
2096*4882a593Smuzhiyun err = -EINVAL;
2097*4882a593Smuzhiyun goto exit;
2098*4882a593Smuzhiyun }
2099*4882a593Smuzhiyun
2100*4882a593Smuzhiyun #if defined(USE_INITIAL_2G_SCAN) || defined(USE_INITIAL_SHORT_DWELL_TIME)
2101*4882a593Smuzhiyun /* Override active_time to reduce scan time if it's first bradcast scan. */
2102*4882a593Smuzhiyun if (is_first_init_2g_scan) {
2103*4882a593Smuzhiyun if (eparams_v2) {
2104*4882a593Smuzhiyun eparams_v2->params.active_time = FIRST_SCAN_ACTIVE_DWELL_TIME_MS;
2105*4882a593Smuzhiyun } else {
2106*4882a593Smuzhiyun eparams->params.active_time = FIRST_SCAN_ACTIVE_DWELL_TIME_MS;
2107*4882a593Smuzhiyun }
2108*4882a593Smuzhiyun }
2109*4882a593Smuzhiyun #endif /* USE_INITIAL_2G_SCAN || USE_INITIAL_SHORT_DWELL_TIME */
2110*4882a593Smuzhiyun
2111*4882a593Smuzhiyun wl_escan_set_type(cfg, WL_SCANTYPE_LEGACY);
2112*4882a593Smuzhiyun if (params_size + sizeof("escan") >= WLC_IOCTL_MEDLEN) {
2113*4882a593Smuzhiyun WL_ERR(("ioctl buffer length not sufficient\n"));
2114*4882a593Smuzhiyun MFREE(cfg->osh, params, params_size);
2115*4882a593Smuzhiyun err = -ENOMEM;
2116*4882a593Smuzhiyun goto exit;
2117*4882a593Smuzhiyun }
2118*4882a593Smuzhiyun
2119*4882a593Smuzhiyun bssidx = wl_get_bssidx_by_wdev(cfg, ndev->ieee80211_ptr);
2120*4882a593Smuzhiyun WL_MSG(ndev->name, "LEGACY_SCAN sync ID: %d, bssidx: %d\n", sync_id, bssidx);
2121*4882a593Smuzhiyun err = wldev_iovar_setbuf(ndev, "escan", params, params_size,
2122*4882a593Smuzhiyun cfg->escan_ioctl_buf, WLC_IOCTL_MEDLEN, NULL);
2123*4882a593Smuzhiyun if (unlikely(err)) {
2124*4882a593Smuzhiyun if (err == BCME_EPERM)
2125*4882a593Smuzhiyun /* Scan Not permitted at this point of time */
2126*4882a593Smuzhiyun WL_DBG((" Escan not permitted at this time (%d)\n", err));
2127*4882a593Smuzhiyun else
2128*4882a593Smuzhiyun WL_ERR((" Escan set error (%d)\n", err));
2129*4882a593Smuzhiyun } else {
2130*4882a593Smuzhiyun DBG_EVENT_LOG((dhd_pub_t *)cfg->pub, WIFI_EVENT_DRIVER_SCAN_REQUESTED);
2131*4882a593Smuzhiyun }
2132*4882a593Smuzhiyun MFREE(cfg->osh, params, params_size);
2133*4882a593Smuzhiyun }
2134*4882a593Smuzhiyun else if (p2p_is_on(cfg) && p2p_scan(cfg)) {
2135*4882a593Smuzhiyun /* P2P SCAN TRIGGER */
2136*4882a593Smuzhiyun if (request->n_channels) {
2137*4882a593Smuzhiyun num_chans = request->n_channels;
2138*4882a593Smuzhiyun WL_SCAN((" chan number : %d\n", num_chans));
2139*4882a593Smuzhiyun chan_mem = (u32)(num_chans * sizeof(*default_chan_list));
2140*4882a593Smuzhiyun default_chan_list = MALLOCZ(cfg->osh, chan_mem);
2141*4882a593Smuzhiyun if (default_chan_list == NULL) {
2142*4882a593Smuzhiyun WL_ERR(("channel list allocation failed \n"));
2143*4882a593Smuzhiyun err = -ENOMEM;
2144*4882a593Smuzhiyun goto exit;
2145*4882a593Smuzhiyun }
2146*4882a593Smuzhiyun /* Populate channels for p2p scanning */
2147*4882a593Smuzhiyun wl_cfgscan_populate_scan_channels(cfg,
2148*4882a593Smuzhiyun request->channels, request->n_channels,
2149*4882a593Smuzhiyun default_chan_list, &num_chans, true, true);
2150*4882a593Smuzhiyun
2151*4882a593Smuzhiyun if (num_chans == SOCIAL_CHAN_CNT && (
2152*4882a593Smuzhiyun (CHSPEC_CHANNEL(default_chan_list[0]) ==
2153*4882a593Smuzhiyun SOCIAL_CHAN_1) &&
2154*4882a593Smuzhiyun (CHSPEC_CHANNEL(default_chan_list[1]) ==
2155*4882a593Smuzhiyun SOCIAL_CHAN_2) &&
2156*4882a593Smuzhiyun (CHSPEC_CHANNEL(default_chan_list[2]) ==
2157*4882a593Smuzhiyun SOCIAL_CHAN_3))) {
2158*4882a593Smuzhiyun /* SOCIAL CHANNELS 1, 6, 11 */
2159*4882a593Smuzhiyun search_state = WL_P2P_DISC_ST_SEARCH;
2160*4882a593Smuzhiyun p2p_scan_purpose = P2P_SCAN_SOCIAL_CHANNEL;
2161*4882a593Smuzhiyun WL_DBG(("P2P SEARCH PHASE START \n"));
2162*4882a593Smuzhiyun } else if (((dev = wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_CONNECTION1)) &&
2163*4882a593Smuzhiyun (wl_get_mode_by_netdev(cfg, dev) == WL_MODE_AP)) ||
2164*4882a593Smuzhiyun ((dev = wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_CONNECTION2)) &&
2165*4882a593Smuzhiyun (wl_get_mode_by_netdev(cfg, dev) == WL_MODE_AP))) {
2166*4882a593Smuzhiyun /* If you are already a GO, then do SEARCH only */
2167*4882a593Smuzhiyun WL_DBG(("Already a GO. Do SEARCH Only"));
2168*4882a593Smuzhiyun search_state = WL_P2P_DISC_ST_SEARCH;
2169*4882a593Smuzhiyun p2p_scan_purpose = P2P_SCAN_NORMAL;
2170*4882a593Smuzhiyun
2171*4882a593Smuzhiyun } else if (num_chans == 1) {
2172*4882a593Smuzhiyun p2p_scan_purpose = P2P_SCAN_CONNECT_TRY;
2173*4882a593Smuzhiyun } else if (num_chans == SOCIAL_CHAN_CNT + 1) {
2174*4882a593Smuzhiyun /* SOCIAL_CHAN_CNT + 1 takes care of the Progressive scan supported by
2175*4882a593Smuzhiyun * the supplicant
2176*4882a593Smuzhiyun */
2177*4882a593Smuzhiyun p2p_scan_purpose = P2P_SCAN_SOCIAL_CHANNEL;
2178*4882a593Smuzhiyun } else {
2179*4882a593Smuzhiyun WL_DBG(("P2P SCAN STATE START \n"));
2180*4882a593Smuzhiyun p2p_scan_purpose = P2P_SCAN_NORMAL;
2181*4882a593Smuzhiyun }
2182*4882a593Smuzhiyun } else {
2183*4882a593Smuzhiyun err = -EINVAL;
2184*4882a593Smuzhiyun goto exit;
2185*4882a593Smuzhiyun }
2186*4882a593Smuzhiyun WL_INFORM_MEM(("p2p_scan num_channels:%d\n", num_chans));
2187*4882a593Smuzhiyun err = wl_cfgp2p_escan(cfg, ndev, ACTIVE_SCAN, num_chans, default_chan_list,
2188*4882a593Smuzhiyun search_state, action,
2189*4882a593Smuzhiyun wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE), NULL,
2190*4882a593Smuzhiyun p2p_scan_purpose);
2191*4882a593Smuzhiyun
2192*4882a593Smuzhiyun if (!err)
2193*4882a593Smuzhiyun cfg->p2p->search_state = search_state;
2194*4882a593Smuzhiyun
2195*4882a593Smuzhiyun MFREE(cfg->osh, default_chan_list, chan_mem);
2196*4882a593Smuzhiyun }
2197*4882a593Smuzhiyun exit:
2198*4882a593Smuzhiyun if (unlikely(err)) {
2199*4882a593Smuzhiyun /* Don't print Error incase of Scan suppress */
2200*4882a593Smuzhiyun if ((err == BCME_EPERM) && cfg->scan_suppressed)
2201*4882a593Smuzhiyun WL_DBG(("Escan failed: Scan Suppressed \n"));
2202*4882a593Smuzhiyun else
2203*4882a593Smuzhiyun WL_ERR(("scan error (%d)\n", err));
2204*4882a593Smuzhiyun }
2205*4882a593Smuzhiyun return err;
2206*4882a593Smuzhiyun }
2207*4882a593Smuzhiyun
2208*4882a593Smuzhiyun s32
wl_do_escan(struct bcm_cfg80211 * cfg,struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_scan_request * request)2209*4882a593Smuzhiyun wl_do_escan(struct bcm_cfg80211 *cfg, struct wiphy *wiphy, struct net_device *ndev,
2210*4882a593Smuzhiyun struct cfg80211_scan_request *request)
2211*4882a593Smuzhiyun {
2212*4882a593Smuzhiyun s32 err = BCME_OK;
2213*4882a593Smuzhiyun s32 passive_scan;
2214*4882a593Smuzhiyun s32 passive_scan_time;
2215*4882a593Smuzhiyun s32 passive_scan_time_org;
2216*4882a593Smuzhiyun wl_scan_results_t *results;
2217*4882a593Smuzhiyun WL_SCAN(("Enter \n"));
2218*4882a593Smuzhiyun
2219*4882a593Smuzhiyun results = wl_escan_get_buf(cfg, FALSE);
2220*4882a593Smuzhiyun results->version = 0;
2221*4882a593Smuzhiyun results->count = 0;
2222*4882a593Smuzhiyun results->buflen = WL_SCAN_RESULTS_FIXED_SIZE;
2223*4882a593Smuzhiyun
2224*4882a593Smuzhiyun cfg->escan_info.ndev = ndev;
2225*4882a593Smuzhiyun cfg->escan_info.wiphy = wiphy;
2226*4882a593Smuzhiyun cfg->escan_info.escan_state = WL_ESCAN_STATE_SCANING;
2227*4882a593Smuzhiyun passive_scan = cfg->active_scan ? 0 : 1;
2228*4882a593Smuzhiyun err = wldev_ioctl_set(ndev, WLC_SET_PASSIVE_SCAN,
2229*4882a593Smuzhiyun &passive_scan, sizeof(passive_scan));
2230*4882a593Smuzhiyun if (unlikely(err)) {
2231*4882a593Smuzhiyun WL_ERR(("error (%d)\n", err));
2232*4882a593Smuzhiyun goto exit;
2233*4882a593Smuzhiyun }
2234*4882a593Smuzhiyun
2235*4882a593Smuzhiyun if (passive_channel_skip) {
2236*4882a593Smuzhiyun
2237*4882a593Smuzhiyun err = wldev_ioctl_get(ndev, WLC_GET_SCAN_PASSIVE_TIME,
2238*4882a593Smuzhiyun &passive_scan_time_org, sizeof(passive_scan_time_org));
2239*4882a593Smuzhiyun if (unlikely(err)) {
2240*4882a593Smuzhiyun WL_ERR(("== error (%d)\n", err));
2241*4882a593Smuzhiyun goto exit;
2242*4882a593Smuzhiyun }
2243*4882a593Smuzhiyun
2244*4882a593Smuzhiyun WL_SCAN(("PASSIVE SCAN time : %d \n", passive_scan_time_org));
2245*4882a593Smuzhiyun
2246*4882a593Smuzhiyun passive_scan_time = 0;
2247*4882a593Smuzhiyun err = wldev_ioctl_set(ndev, WLC_SET_SCAN_PASSIVE_TIME,
2248*4882a593Smuzhiyun &passive_scan_time, sizeof(passive_scan_time));
2249*4882a593Smuzhiyun if (unlikely(err)) {
2250*4882a593Smuzhiyun WL_ERR(("== error (%d)\n", err));
2251*4882a593Smuzhiyun goto exit;
2252*4882a593Smuzhiyun }
2253*4882a593Smuzhiyun
2254*4882a593Smuzhiyun WL_SCAN(("PASSIVE SCAN SKIPED!! (passive_channel_skip:%d) \n",
2255*4882a593Smuzhiyun passive_channel_skip));
2256*4882a593Smuzhiyun }
2257*4882a593Smuzhiyun
2258*4882a593Smuzhiyun err = wl_run_escan(cfg, ndev, request, WL_SCAN_ACTION_START);
2259*4882a593Smuzhiyun
2260*4882a593Smuzhiyun if (passive_channel_skip) {
2261*4882a593Smuzhiyun err = wldev_ioctl_set(ndev, WLC_SET_SCAN_PASSIVE_TIME,
2262*4882a593Smuzhiyun &passive_scan_time_org, sizeof(passive_scan_time_org));
2263*4882a593Smuzhiyun if (unlikely(err)) {
2264*4882a593Smuzhiyun WL_ERR(("== error (%d)\n", err));
2265*4882a593Smuzhiyun goto exit;
2266*4882a593Smuzhiyun }
2267*4882a593Smuzhiyun
2268*4882a593Smuzhiyun WL_SCAN(("PASSIVE SCAN RECOVERED!! (passive_scan_time_org:%d) \n",
2269*4882a593Smuzhiyun passive_scan_time_org));
2270*4882a593Smuzhiyun }
2271*4882a593Smuzhiyun
2272*4882a593Smuzhiyun exit:
2273*4882a593Smuzhiyun return err;
2274*4882a593Smuzhiyun }
2275*4882a593Smuzhiyun
2276*4882a593Smuzhiyun static s32
wl_get_scan_timeout_val(struct bcm_cfg80211 * cfg)2277*4882a593Smuzhiyun wl_get_scan_timeout_val(struct bcm_cfg80211 *cfg)
2278*4882a593Smuzhiyun {
2279*4882a593Smuzhiyun u32 scan_timer_interval_ms = WL_SCAN_TIMER_INTERVAL_MS;
2280*4882a593Smuzhiyun
2281*4882a593Smuzhiyun #ifdef WES_SUPPORT
2282*4882a593Smuzhiyun #ifdef CUSTOMER_SCAN_TIMEOUT_SETTING
2283*4882a593Smuzhiyun if ((cfg->custom_scan_channel_time > DHD_SCAN_ASSOC_ACTIVE_TIME) |
2284*4882a593Smuzhiyun (cfg->custom_scan_unassoc_time > DHD_SCAN_UNASSOC_ACTIVE_TIME) |
2285*4882a593Smuzhiyun (cfg->custom_scan_passive_time > DHD_SCAN_PASSIVE_TIME) |
2286*4882a593Smuzhiyun (cfg->custom_scan_home_time > DHD_SCAN_HOME_TIME) |
2287*4882a593Smuzhiyun (cfg->custom_scan_home_away_time > DHD_SCAN_HOME_AWAY_TIME)) {
2288*4882a593Smuzhiyun scan_timer_interval_ms = CUSTOMER_WL_SCAN_TIMER_INTERVAL_MS;
2289*4882a593Smuzhiyun }
2290*4882a593Smuzhiyun #endif /* CUSTOMER_SCAN_TIMEOUT_SETTING */
2291*4882a593Smuzhiyun #endif /* WES_SUPPORT */
2292*4882a593Smuzhiyun
2293*4882a593Smuzhiyun /* If NAN is enabled adding +10 sec to the existing timeout value */
2294*4882a593Smuzhiyun #ifdef WL_NAN
2295*4882a593Smuzhiyun if (wl_cfgnan_is_enabled(cfg)) {
2296*4882a593Smuzhiyun scan_timer_interval_ms += WL_SCAN_TIMER_INTERVAL_MS_NAN;
2297*4882a593Smuzhiyun }
2298*4882a593Smuzhiyun #endif /* WL_NAN */
2299*4882a593Smuzhiyun /* Additional time to scan 6GHz band channels */
2300*4882a593Smuzhiyun #ifdef WL_6G_BAND
2301*4882a593Smuzhiyun if (cfg->band_6g_supported) {
2302*4882a593Smuzhiyun scan_timer_interval_ms += WL_SCAN_TIMER_INTERVAL_MS_6G;
2303*4882a593Smuzhiyun }
2304*4882a593Smuzhiyun #endif /* WL_6G_BAND */
2305*4882a593Smuzhiyun WL_MEM(("scan_timer_interval_ms %d\n", scan_timer_interval_ms));
2306*4882a593Smuzhiyun return scan_timer_interval_ms;
2307*4882a593Smuzhiyun }
2308*4882a593Smuzhiyun
2309*4882a593Smuzhiyun #define SCAN_EBUSY_RETRY_LIMIT 20
2310*4882a593Smuzhiyun static s32
wl_cfgscan_handle_scanbusy(struct bcm_cfg80211 * cfg,struct net_device * ndev,s32 err)2311*4882a593Smuzhiyun wl_cfgscan_handle_scanbusy(struct bcm_cfg80211 *cfg, struct net_device *ndev, s32 err)
2312*4882a593Smuzhiyun {
2313*4882a593Smuzhiyun s32 scanbusy_err = 0;
2314*4882a593Smuzhiyun static u32 busy_count = 0;
2315*4882a593Smuzhiyun
2316*4882a593Smuzhiyun if (!err) {
2317*4882a593Smuzhiyun busy_count = 0;
2318*4882a593Smuzhiyun return scanbusy_err;
2319*4882a593Smuzhiyun }
2320*4882a593Smuzhiyun if (err == BCME_BUSY || err == BCME_NOTREADY) {
2321*4882a593Smuzhiyun WL_ERR(("Scan err = (%d), busy?%d\n", err, -EBUSY));
2322*4882a593Smuzhiyun scanbusy_err = -EBUSY;
2323*4882a593Smuzhiyun } else if ((err == BCME_EPERM) && cfg->scan_suppressed) {
2324*4882a593Smuzhiyun WL_ERR(("Scan not permitted due to scan suppress\n"));
2325*4882a593Smuzhiyun scanbusy_err = -EPERM;
2326*4882a593Smuzhiyun } else {
2327*4882a593Smuzhiyun /* For all other fw errors, use a generic error code as return
2328*4882a593Smuzhiyun * value to cfg80211 stack
2329*4882a593Smuzhiyun */
2330*4882a593Smuzhiyun scanbusy_err = -EAGAIN;
2331*4882a593Smuzhiyun }
2332*4882a593Smuzhiyun
2333*4882a593Smuzhiyun /* if continuous busy state, clear assoc type in FW by disassoc cmd */
2334*4882a593Smuzhiyun if (scanbusy_err == -EBUSY) {
2335*4882a593Smuzhiyun /* Flush FW preserve buffer logs for checking failure */
2336*4882a593Smuzhiyun if (busy_count++ > (SCAN_EBUSY_RETRY_LIMIT/5)) {
2337*4882a593Smuzhiyun wl_flush_fw_log_buffer(ndev, FW_LOGSET_MASK_ALL);
2338*4882a593Smuzhiyun }
2339*4882a593Smuzhiyun if (busy_count > SCAN_EBUSY_RETRY_LIMIT) {
2340*4882a593Smuzhiyun struct ether_addr bssid;
2341*4882a593Smuzhiyun s32 ret = 0;
2342*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
2343*4882a593Smuzhiyun dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
2344*4882a593Smuzhiyun if (dhd_query_bus_erros(dhdp)) {
2345*4882a593Smuzhiyun return BCME_NOTREADY;
2346*4882a593Smuzhiyun }
2347*4882a593Smuzhiyun dhdp->scan_busy_occurred = TRUE;
2348*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
2349*4882a593Smuzhiyun busy_count = 0;
2350*4882a593Smuzhiyun WL_ERR(("Unusual continuous EBUSY error, %d %d %d %d %d %d %d %d %d\n",
2351*4882a593Smuzhiyun wl_get_drv_status(cfg, SCANNING, ndev),
2352*4882a593Smuzhiyun wl_get_drv_status(cfg, SCAN_ABORTING, ndev),
2353*4882a593Smuzhiyun wl_get_drv_status(cfg, CONNECTING, ndev),
2354*4882a593Smuzhiyun wl_get_drv_status(cfg, CONNECTED, ndev),
2355*4882a593Smuzhiyun wl_get_drv_status(cfg, DISCONNECTING, ndev),
2356*4882a593Smuzhiyun wl_get_drv_status(cfg, AP_CREATING, ndev),
2357*4882a593Smuzhiyun wl_get_drv_status(cfg, AP_CREATED, ndev),
2358*4882a593Smuzhiyun wl_get_drv_status(cfg, SENDING_ACT_FRM, ndev),
2359*4882a593Smuzhiyun wl_get_drv_status(cfg, SENDING_ACT_FRM, ndev)));
2360*4882a593Smuzhiyun
2361*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
2362*4882a593Smuzhiyun #if defined(DHD_DEBUG) && defined(DHD_FW_COREDUMP)
2363*4882a593Smuzhiyun if (dhdp->memdump_enabled) {
2364*4882a593Smuzhiyun dhdp->memdump_type = DUMP_TYPE_SCAN_BUSY;
2365*4882a593Smuzhiyun dhd_bus_mem_dump(dhdp);
2366*4882a593Smuzhiyun }
2367*4882a593Smuzhiyun #endif /* DHD_DEBUG && DHD_FW_COREDUMP */
2368*4882a593Smuzhiyun dhdp->hang_reason = HANG_REASON_SCAN_BUSY;
2369*4882a593Smuzhiyun
2370*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(OEM_ANDROID)
2371*4882a593Smuzhiyun dhd_os_send_hang_message(dhdp);
2372*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(OEM_ANDROID) */
2373*4882a593Smuzhiyun
2374*4882a593Smuzhiyun #if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && \
2375*4882a593Smuzhiyun defined(OEM_ANDROID))
2376*4882a593Smuzhiyun WL_ERR(("%s: HANG event is unsupported\n", __FUNCTION__));
2377*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) && OEM_ANDROID */
2378*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
2379*4882a593Smuzhiyun
2380*4882a593Smuzhiyun bzero(&bssid, sizeof(bssid));
2381*4882a593Smuzhiyun if ((ret = wldev_ioctl_get(ndev, WLC_GET_BSSID,
2382*4882a593Smuzhiyun &bssid, ETHER_ADDR_LEN)) == 0) {
2383*4882a593Smuzhiyun WL_ERR(("FW is connected with " MACDBG "\n",
2384*4882a593Smuzhiyun MAC2STRDBG(bssid.octet)));
2385*4882a593Smuzhiyun } else {
2386*4882a593Smuzhiyun WL_ERR(("GET BSSID failed with %d\n", ret));
2387*4882a593Smuzhiyun }
2388*4882a593Smuzhiyun
2389*4882a593Smuzhiyun /* To support GO, wl_cfgscan_cancel_scan()
2390*4882a593Smuzhiyun * is needed instead of wl_cfg80211_disconnect()
2391*4882a593Smuzhiyun */
2392*4882a593Smuzhiyun wl_cfgscan_cancel_scan(cfg);
2393*4882a593Smuzhiyun
2394*4882a593Smuzhiyun } else {
2395*4882a593Smuzhiyun /* Hold the context for 400msec, so that 10 subsequent scans
2396*4882a593Smuzhiyun * can give a buffer of 4sec which is enough to
2397*4882a593Smuzhiyun * cover any on-going scan in the firmware
2398*4882a593Smuzhiyun */
2399*4882a593Smuzhiyun WL_DBG(("Enforcing delay for EBUSY case \n"));
2400*4882a593Smuzhiyun msleep(400);
2401*4882a593Smuzhiyun }
2402*4882a593Smuzhiyun } else {
2403*4882a593Smuzhiyun busy_count = 0;
2404*4882a593Smuzhiyun }
2405*4882a593Smuzhiyun
2406*4882a593Smuzhiyun return scanbusy_err;
2407*4882a593Smuzhiyun }
2408*4882a593Smuzhiyun
2409*4882a593Smuzhiyun s32
__wl_cfg80211_scan(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_scan_request * request,struct cfg80211_ssid * this_ssid)2410*4882a593Smuzhiyun __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
2411*4882a593Smuzhiyun struct cfg80211_scan_request *request,
2412*4882a593Smuzhiyun struct cfg80211_ssid *this_ssid)
2413*4882a593Smuzhiyun {
2414*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
2415*4882a593Smuzhiyun struct cfg80211_ssid *ssids;
2416*4882a593Smuzhiyun bool p2p_ssid;
2417*4882a593Smuzhiyun #ifdef WL11U
2418*4882a593Smuzhiyun bcm_tlv_t *interworking_ie;
2419*4882a593Smuzhiyun #endif
2420*4882a593Smuzhiyun s32 err = 0;
2421*4882a593Smuzhiyun s32 bssidx = -1;
2422*4882a593Smuzhiyun s32 i;
2423*4882a593Smuzhiyun bool escan_req_failed = false;
2424*4882a593Smuzhiyun s32 scanbusy_err = 0;
2425*4882a593Smuzhiyun
2426*4882a593Smuzhiyun unsigned long flags;
2427*4882a593Smuzhiyun #ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST
2428*4882a593Smuzhiyun struct net_device *remain_on_channel_ndev = NULL;
2429*4882a593Smuzhiyun #endif
2430*4882a593Smuzhiyun /*
2431*4882a593Smuzhiyun * Hostapd triggers scan before starting automatic channel selection
2432*4882a593Smuzhiyun * to collect channel characteristics. However firmware scan engine
2433*4882a593Smuzhiyun * doesn't support any channel characteristics collection along with
2434*4882a593Smuzhiyun * scan. Hence return scan success.
2435*4882a593Smuzhiyun */
2436*4882a593Smuzhiyun if (request && (scan_req_iftype(request) == NL80211_IFTYPE_AP)) {
2437*4882a593Smuzhiyun WL_DBG(("Scan Command on SoftAP Interface. Ignoring...\n"));
2438*4882a593Smuzhiyun // terence 20161023: let it scan in SoftAP mode
2439*4882a593Smuzhiyun // return 0;
2440*4882a593Smuzhiyun }
2441*4882a593Smuzhiyun
2442*4882a593Smuzhiyun if (request && request->n_ssids > WL_SCAN_PARAMS_SSID_MAX) {
2443*4882a593Smuzhiyun WL_ERR(("request null or n_ssids > WL_SCAN_PARAMS_SSID_MAX\n"));
2444*4882a593Smuzhiyun return -EOPNOTSUPP;
2445*4882a593Smuzhiyun }
2446*4882a593Smuzhiyun
2447*4882a593Smuzhiyun ndev = ndev_to_wlc_ndev(ndev, cfg);
2448*4882a593Smuzhiyun
2449*4882a593Smuzhiyun if (WL_DRV_STATUS_SENDING_AF_FRM_EXT(cfg)) {
2450*4882a593Smuzhiyun WL_ERR(("Sending Action Frames. Try it again.\n"));
2451*4882a593Smuzhiyun return -EAGAIN;
2452*4882a593Smuzhiyun }
2453*4882a593Smuzhiyun
2454*4882a593Smuzhiyun WL_DBG(("Enter wiphy (%p)\n", wiphy));
2455*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
2456*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, SCANNING)) {
2457*4882a593Smuzhiyun if (cfg->scan_request == NULL) {
2458*4882a593Smuzhiyun wl_clr_drv_status_all(cfg, SCANNING);
2459*4882a593Smuzhiyun WL_DBG(("<<<<<<<<<<<Force Clear Scanning Status>>>>>>>>>>>\n"));
2460*4882a593Smuzhiyun } else {
2461*4882a593Smuzhiyun WL_ERR(("Scanning already\n"));
2462*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
2463*4882a593Smuzhiyun return -EAGAIN;
2464*4882a593Smuzhiyun }
2465*4882a593Smuzhiyun }
2466*4882a593Smuzhiyun if (wl_get_drv_status(cfg, SCAN_ABORTING, ndev)) {
2467*4882a593Smuzhiyun WL_ERR(("Scanning being aborted\n"));
2468*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
2469*4882a593Smuzhiyun return -EAGAIN;
2470*4882a593Smuzhiyun }
2471*4882a593Smuzhiyun
2472*4882a593Smuzhiyun if (cfg->loc.in_progress) {
2473*4882a593Smuzhiyun /* Listen in progress, avoid new scan trigger */
2474*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
2475*4882a593Smuzhiyun return -EBUSY;
2476*4882a593Smuzhiyun }
2477*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
2478*4882a593Smuzhiyun
2479*4882a593Smuzhiyun #ifdef WL_BCNRECV
2480*4882a593Smuzhiyun /* check fakeapscan in progress then abort */
2481*4882a593Smuzhiyun wl_android_bcnrecv_stop(ndev, WL_BCNRECV_SCANBUSY);
2482*4882a593Smuzhiyun #endif /* WL_BCNRECV */
2483*4882a593Smuzhiyun
2484*4882a593Smuzhiyun #ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST
2485*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
2486*4882a593Smuzhiyun remain_on_channel_ndev = wl_cfg80211_get_remain_on_channel_ndev(cfg);
2487*4882a593Smuzhiyun if (remain_on_channel_ndev) {
2488*4882a593Smuzhiyun WL_DBG(("Remain_on_channel bit is set, somehow it didn't get cleared\n"));
2489*4882a593Smuzhiyun _wl_cfgscan_cancel_scan(cfg);
2490*4882a593Smuzhiyun }
2491*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
2492*4882a593Smuzhiyun #endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */
2493*4882a593Smuzhiyun
2494*4882a593Smuzhiyun #ifdef P2P_LISTEN_OFFLOADING
2495*4882a593Smuzhiyun wl_cfg80211_cancel_p2plo(cfg);
2496*4882a593Smuzhiyun #endif /* P2P_LISTEN_OFFLOADING */
2497*4882a593Smuzhiyun
2498*4882a593Smuzhiyun #ifdef WL_SDO
2499*4882a593Smuzhiyun if (wl_get_p2p_status(cfg, DISC_IN_PROGRESS)) {
2500*4882a593Smuzhiyun wl_cfg80211_pause_sdo(ndev, cfg);
2501*4882a593Smuzhiyun }
2502*4882a593Smuzhiyun #endif
2503*4882a593Smuzhiyun
2504*4882a593Smuzhiyun if (request) { /* scan bss */
2505*4882a593Smuzhiyun ssids = request->ssids;
2506*4882a593Smuzhiyun p2p_ssid = false;
2507*4882a593Smuzhiyun for (i = 0; i < request->n_ssids; i++) {
2508*4882a593Smuzhiyun if (ssids[i].ssid_len &&
2509*4882a593Smuzhiyun IS_P2P_SSID(ssids[i].ssid, ssids[i].ssid_len)) {
2510*4882a593Smuzhiyun /* P2P Scan */
2511*4882a593Smuzhiyun #ifdef WL_BLOCK_P2P_SCAN_ON_STA
2512*4882a593Smuzhiyun if (!(IS_P2P_IFACE(request->wdev))) {
2513*4882a593Smuzhiyun /* P2P scan on non-p2p iface. Fail scan */
2514*4882a593Smuzhiyun WL_ERR(("p2p_search on non p2p iface\n"));
2515*4882a593Smuzhiyun goto scan_out;
2516*4882a593Smuzhiyun }
2517*4882a593Smuzhiyun #endif /* WL_BLOCK_P2P_SCAN_ON_STA */
2518*4882a593Smuzhiyun p2p_ssid = true;
2519*4882a593Smuzhiyun break;
2520*4882a593Smuzhiyun }
2521*4882a593Smuzhiyun }
2522*4882a593Smuzhiyun if (p2p_ssid) {
2523*4882a593Smuzhiyun if (cfg->p2p_supported) {
2524*4882a593Smuzhiyun /* p2p scan trigger */
2525*4882a593Smuzhiyun if (p2p_on(cfg) == false) {
2526*4882a593Smuzhiyun /* p2p on at the first time */
2527*4882a593Smuzhiyun p2p_on(cfg) = true;
2528*4882a593Smuzhiyun wl_cfgp2p_set_firm_p2p(cfg);
2529*4882a593Smuzhiyun #if defined(P2P_IE_MISSING_FIX)
2530*4882a593Smuzhiyun cfg->p2p_prb_noti = false;
2531*4882a593Smuzhiyun #endif
2532*4882a593Smuzhiyun }
2533*4882a593Smuzhiyun wl_clr_p2p_status(cfg, GO_NEG_PHASE);
2534*4882a593Smuzhiyun WL_DBG(("P2P: GO_NEG_PHASE status cleared \n"));
2535*4882a593Smuzhiyun p2p_scan(cfg) = true;
2536*4882a593Smuzhiyun }
2537*4882a593Smuzhiyun } else {
2538*4882a593Smuzhiyun /* legacy scan trigger
2539*4882a593Smuzhiyun * So, we have to disable p2p discovery if p2p discovery is on
2540*4882a593Smuzhiyun */
2541*4882a593Smuzhiyun if (cfg->p2p_supported) {
2542*4882a593Smuzhiyun p2p_scan(cfg) = false;
2543*4882a593Smuzhiyun /* If Netdevice is not equals to primary and p2p is on
2544*4882a593Smuzhiyun * , we will do p2p scan using P2PAPI_BSSCFG_DEVICE.
2545*4882a593Smuzhiyun */
2546*4882a593Smuzhiyun
2547*4882a593Smuzhiyun if (p2p_scan(cfg) == false) {
2548*4882a593Smuzhiyun if (wl_get_p2p_status(cfg, DISCOVERY_ON)) {
2549*4882a593Smuzhiyun err = wl_cfgp2p_discover_enable_search(cfg,
2550*4882a593Smuzhiyun false);
2551*4882a593Smuzhiyun if (unlikely(err)) {
2552*4882a593Smuzhiyun goto scan_out;
2553*4882a593Smuzhiyun }
2554*4882a593Smuzhiyun
2555*4882a593Smuzhiyun }
2556*4882a593Smuzhiyun }
2557*4882a593Smuzhiyun }
2558*4882a593Smuzhiyun if (!cfg->p2p_supported || !p2p_scan(cfg)) {
2559*4882a593Smuzhiyun if ((bssidx = wl_get_bssidx_by_wdev(cfg,
2560*4882a593Smuzhiyun ndev->ieee80211_ptr)) < 0) {
2561*4882a593Smuzhiyun WL_ERR(("Find p2p index from ndev(%p) failed\n",
2562*4882a593Smuzhiyun ndev));
2563*4882a593Smuzhiyun err = BCME_ERROR;
2564*4882a593Smuzhiyun goto scan_out;
2565*4882a593Smuzhiyun }
2566*4882a593Smuzhiyun #ifdef WL11U
2567*4882a593Smuzhiyun if (request && (interworking_ie = wl_cfg80211_find_interworking_ie(
2568*4882a593Smuzhiyun request->ie, request->ie_len)) != NULL) {
2569*4882a593Smuzhiyun if ((err = wl_cfg80211_add_iw_ie(cfg, ndev, bssidx,
2570*4882a593Smuzhiyun VNDR_IE_CUSTOM_FLAG, interworking_ie->id,
2571*4882a593Smuzhiyun interworking_ie->data,
2572*4882a593Smuzhiyun interworking_ie->len)) != BCME_OK) {
2573*4882a593Smuzhiyun WL_ERR(("Failed to add interworking IE"));
2574*4882a593Smuzhiyun }
2575*4882a593Smuzhiyun } else if (cfg->wl11u) {
2576*4882a593Smuzhiyun /* we have to clear IW IE and disable gratuitous APR */
2577*4882a593Smuzhiyun wl_cfg80211_clear_iw_ie(cfg, ndev, bssidx);
2578*4882a593Smuzhiyun err = wldev_iovar_setint_bsscfg(ndev, "grat_arp",
2579*4882a593Smuzhiyun 0, bssidx);
2580*4882a593Smuzhiyun /* we don't care about error here
2581*4882a593Smuzhiyun * because the only failure case is unsupported,
2582*4882a593Smuzhiyun * which is fine
2583*4882a593Smuzhiyun */
2584*4882a593Smuzhiyun if (unlikely(err)) {
2585*4882a593Smuzhiyun WL_ERR(("Set grat_arp failed:(%d) Ignore!\n", err));
2586*4882a593Smuzhiyun }
2587*4882a593Smuzhiyun cfg->wl11u = FALSE;
2588*4882a593Smuzhiyun }
2589*4882a593Smuzhiyun #endif /* WL11U */
2590*4882a593Smuzhiyun if (request) {
2591*4882a593Smuzhiyun err = wl_cfg80211_set_mgmt_vndr_ies(cfg,
2592*4882a593Smuzhiyun ndev_to_cfgdev(ndev), bssidx, VNDR_IE_PRBREQ_FLAG,
2593*4882a593Smuzhiyun request->ie, request->ie_len);
2594*4882a593Smuzhiyun }
2595*4882a593Smuzhiyun
2596*4882a593Smuzhiyun if (unlikely(err)) {
2597*4882a593Smuzhiyun // terence 20161023: let it scan in SoftAP mode
2598*4882a593Smuzhiyun // goto scan_out;
2599*4882a593Smuzhiyun }
2600*4882a593Smuzhiyun
2601*4882a593Smuzhiyun }
2602*4882a593Smuzhiyun }
2603*4882a593Smuzhiyun } else { /* scan in ibss */
2604*4882a593Smuzhiyun ssids = this_ssid;
2605*4882a593Smuzhiyun }
2606*4882a593Smuzhiyun
2607*4882a593Smuzhiyun WL_TRACE_HW4(("START SCAN\n"));
2608*4882a593Smuzhiyun
2609*4882a593Smuzhiyun #if defined(BCMDONGLEHOST) && defined(OEM_ANDROID)
2610*4882a593Smuzhiyun DHD_OS_SCAN_WAKE_LOCK_TIMEOUT((dhd_pub_t *)(cfg->pub),
2611*4882a593Smuzhiyun wl_get_scan_timeout_val(cfg) + SCAN_WAKE_LOCK_MARGIN_MS);
2612*4882a593Smuzhiyun DHD_DISABLE_RUNTIME_PM((dhd_pub_t *)(cfg->pub));
2613*4882a593Smuzhiyun #endif
2614*4882a593Smuzhiyun
2615*4882a593Smuzhiyun if (cfg->p2p_supported) {
2616*4882a593Smuzhiyun if (request && p2p_on(cfg) && p2p_scan(cfg)) {
2617*4882a593Smuzhiyun
2618*4882a593Smuzhiyun #ifdef WL_SDO
2619*4882a593Smuzhiyun if (wl_get_p2p_status(cfg, DISC_IN_PROGRESS)) {
2620*4882a593Smuzhiyun /* We shouldn't be getting p2p_find while discovery
2621*4882a593Smuzhiyun * offload is in progress
2622*4882a593Smuzhiyun */
2623*4882a593Smuzhiyun WL_SD(("P2P_FIND: Discovery offload is in progress."
2624*4882a593Smuzhiyun " Do nothing\n"));
2625*4882a593Smuzhiyun err = -EINVAL;
2626*4882a593Smuzhiyun goto scan_out;
2627*4882a593Smuzhiyun }
2628*4882a593Smuzhiyun #endif
2629*4882a593Smuzhiyun /* find my listen channel */
2630*4882a593Smuzhiyun cfg->afx_hdl->my_listen_chan =
2631*4882a593Smuzhiyun wl_find_listen_channel(cfg, request->ie,
2632*4882a593Smuzhiyun request->ie_len);
2633*4882a593Smuzhiyun err = wl_cfgp2p_enable_discovery(cfg, ndev,
2634*4882a593Smuzhiyun request->ie, request->ie_len);
2635*4882a593Smuzhiyun
2636*4882a593Smuzhiyun if (unlikely(err)) {
2637*4882a593Smuzhiyun goto scan_out;
2638*4882a593Smuzhiyun }
2639*4882a593Smuzhiyun }
2640*4882a593Smuzhiyun }
2641*4882a593Smuzhiyun
2642*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
2643*4882a593Smuzhiyun err = wl_ext_in4way_sync(ndev, STA_FAKE_SCAN_IN_CONNECT, WL_EXT_STATUS_SCANNING, NULL);
2644*4882a593Smuzhiyun if (err) {
2645*4882a593Smuzhiyun wl_event_msg_t msg;
2646*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
2647*4882a593Smuzhiyun bzero(&msg, sizeof(wl_event_msg_t));
2648*4882a593Smuzhiyun msg.event_type = hton32(WLC_E_ESCAN_RESULT);
2649*4882a593Smuzhiyun msg.status = hton32(WLC_E_STATUS_SUCCESS);
2650*4882a593Smuzhiyun wl_cfg80211_event(ndev, &msg, NULL);
2651*4882a593Smuzhiyun goto scan_success;
2652*4882a593Smuzhiyun }
2653*4882a593Smuzhiyun #endif
2654*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
2655*4882a593Smuzhiyun err = wl_do_escan(cfg, wiphy, ndev, request);
2656*4882a593Smuzhiyun if (likely(!err)) {
2657*4882a593Smuzhiyun goto scan_success;
2658*4882a593Smuzhiyun } else {
2659*4882a593Smuzhiyun escan_req_failed = true;
2660*4882a593Smuzhiyun goto scan_out;
2661*4882a593Smuzhiyun }
2662*4882a593Smuzhiyun
2663*4882a593Smuzhiyun scan_success:
2664*4882a593Smuzhiyun wl_cfgscan_handle_scanbusy(cfg, ndev, BCME_OK);
2665*4882a593Smuzhiyun cfg->scan_request = request;
2666*4882a593Smuzhiyun LOG_TS(cfg, scan_start);
2667*4882a593Smuzhiyun wl_set_drv_status(cfg, SCANNING, ndev);
2668*4882a593Smuzhiyun /* Arm the timer */
2669*4882a593Smuzhiyun mod_timer(&cfg->scan_timeout,
2670*4882a593Smuzhiyun jiffies + msecs_to_jiffies(wl_get_scan_timeout_val(cfg)));
2671*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
2672*4882a593Smuzhiyun return 0;
2673*4882a593Smuzhiyun
2674*4882a593Smuzhiyun scan_out:
2675*4882a593Smuzhiyun if (escan_req_failed) {
2676*4882a593Smuzhiyun WL_CFG_DRV_LOCK(&cfg->cfgdrv_lock, flags);
2677*4882a593Smuzhiyun cfg->scan_request = NULL;
2678*4882a593Smuzhiyun WL_CFG_DRV_UNLOCK(&cfg->cfgdrv_lock, flags);
2679*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
2680*4882a593Smuzhiyun /* Handling for scan busy errors */
2681*4882a593Smuzhiyun scanbusy_err = wl_cfgscan_handle_scanbusy(cfg, ndev, err);
2682*4882a593Smuzhiyun if (scanbusy_err == BCME_NOTREADY) {
2683*4882a593Smuzhiyun /* In case of bus failures avoid ioctl calls */
2684*4882a593Smuzhiyun
2685*4882a593Smuzhiyun #if defined(BCMDONGLEHOST) && defined(OEM_ANDROID)
2686*4882a593Smuzhiyun DHD_OS_SCAN_WAKE_UNLOCK((dhd_pub_t *)(cfg->pub));
2687*4882a593Smuzhiyun DHD_ENABLE_RUNTIME_PM((dhd_pub_t *)(cfg->pub));
2688*4882a593Smuzhiyun #endif
2689*4882a593Smuzhiyun
2690*4882a593Smuzhiyun return -ENODEV;
2691*4882a593Smuzhiyun }
2692*4882a593Smuzhiyun err = scanbusy_err;
2693*4882a593Smuzhiyun }
2694*4882a593Smuzhiyun
2695*4882a593Smuzhiyun #if defined(BCMDONGLEHOST) && defined(OEM_ANDROID)
2696*4882a593Smuzhiyun DHD_OS_SCAN_WAKE_UNLOCK((dhd_pub_t *)(cfg->pub));
2697*4882a593Smuzhiyun DHD_ENABLE_RUNTIME_PM((dhd_pub_t *)(cfg->pub));
2698*4882a593Smuzhiyun #endif
2699*4882a593Smuzhiyun
2700*4882a593Smuzhiyun #ifdef WL_SDO
2701*4882a593Smuzhiyun if (wl_get_p2p_status(cfg, DISC_IN_PROGRESS)) {
2702*4882a593Smuzhiyun wl_cfg80211_resume_sdo(ndev, cfg);
2703*4882a593Smuzhiyun }
2704*4882a593Smuzhiyun #endif
2705*4882a593Smuzhiyun return err;
2706*4882a593Smuzhiyun }
2707*4882a593Smuzhiyun
2708*4882a593Smuzhiyun s32
2709*4882a593Smuzhiyun #if defined(WL_CFG80211_P2P_DEV_IF)
wl_cfg80211_scan(struct wiphy * wiphy,struct cfg80211_scan_request * request)2710*4882a593Smuzhiyun wl_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
2711*4882a593Smuzhiyun #else
2712*4882a593Smuzhiyun wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
2713*4882a593Smuzhiyun struct cfg80211_scan_request *request)
2714*4882a593Smuzhiyun #endif /* WL_CFG80211_P2P_DEV_IF */
2715*4882a593Smuzhiyun {
2716*4882a593Smuzhiyun s32 err = 0;
2717*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
2718*4882a593Smuzhiyun #if defined(WL_CFG80211_P2P_DEV_IF)
2719*4882a593Smuzhiyun struct net_device *ndev = wdev_to_wlc_ndev(request->wdev, cfg);
2720*4882a593Smuzhiyun #endif /* WL_CFG80211_P2P_DEV_IF */
2721*4882a593Smuzhiyun
2722*4882a593Smuzhiyun WL_DBG(("Enter\n"));
2723*4882a593Smuzhiyun RETURN_EIO_IF_NOT_UP(cfg);
2724*4882a593Smuzhiyun
2725*4882a593Smuzhiyun #ifdef DHD_IFDEBUG
2726*4882a593Smuzhiyun #ifdef WL_CFG80211_P2P_DEV_IF
2727*4882a593Smuzhiyun PRINT_WDEV_INFO(request->wdev);
2728*4882a593Smuzhiyun #else
2729*4882a593Smuzhiyun PRINT_WDEV_INFO(ndev);
2730*4882a593Smuzhiyun #endif /* WL_CFG80211_P2P_DEV_IF */
2731*4882a593Smuzhiyun #endif /* DHD_IFDEBUG */
2732*4882a593Smuzhiyun
2733*4882a593Smuzhiyun if (ndev == bcmcfg_to_prmry_ndev(cfg)) {
2734*4882a593Smuzhiyun if (wl_cfg_multip2p_operational(cfg)) {
2735*4882a593Smuzhiyun WL_ERR(("wlan0 scan failed, p2p devices are operational"));
2736*4882a593Smuzhiyun return -ENODEV;
2737*4882a593Smuzhiyun }
2738*4882a593Smuzhiyun }
2739*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
2740*4882a593Smuzhiyun err = wl_ext_in4way_sync(ndev_to_wlc_ndev(ndev, cfg), STA_NO_SCAN_IN4WAY,
2741*4882a593Smuzhiyun WL_EXT_STATUS_SCAN, NULL);
2742*4882a593Smuzhiyun if (err) {
2743*4882a593Smuzhiyun WL_SCAN(("scan suppressed %d\n", err));
2744*4882a593Smuzhiyun return err;
2745*4882a593Smuzhiyun }
2746*4882a593Smuzhiyun #endif
2747*4882a593Smuzhiyun
2748*4882a593Smuzhiyun err = __wl_cfg80211_scan(wiphy, ndev, request, NULL);
2749*4882a593Smuzhiyun if (unlikely(err)) {
2750*4882a593Smuzhiyun WL_ERR(("scan error (%d)\n", err));
2751*4882a593Smuzhiyun }
2752*4882a593Smuzhiyun #ifdef WL_DRV_AVOID_SCANCACHE
2753*4882a593Smuzhiyun /* Reset roam cache after successful scan request */
2754*4882a593Smuzhiyun #ifdef ROAM_CHANNEL_CACHE
2755*4882a593Smuzhiyun if (!err) {
2756*4882a593Smuzhiyun reset_roam_cache(cfg);
2757*4882a593Smuzhiyun }
2758*4882a593Smuzhiyun #endif /* ROAM_CHANNEL_CACHE */
2759*4882a593Smuzhiyun #endif /* WL_DRV_AVOID_SCANCACHE */
2760*4882a593Smuzhiyun return err;
2761*4882a593Smuzhiyun }
2762*4882a593Smuzhiyun
2763*4882a593Smuzhiyun /* Note: This API should be invoked with scan_sync mutex
2764*4882a593Smuzhiyun * held so that scan_request data structures doesn't
2765*4882a593Smuzhiyun * get modified in between.
2766*4882a593Smuzhiyun */
2767*4882a593Smuzhiyun struct wireless_dev *
wl_get_scan_wdev(struct bcm_cfg80211 * cfg)2768*4882a593Smuzhiyun wl_get_scan_wdev(struct bcm_cfg80211 *cfg)
2769*4882a593Smuzhiyun {
2770*4882a593Smuzhiyun struct wireless_dev *wdev = NULL;
2771*4882a593Smuzhiyun
2772*4882a593Smuzhiyun if (!cfg) {
2773*4882a593Smuzhiyun WL_ERR(("cfg ptr null\n"));
2774*4882a593Smuzhiyun return NULL;
2775*4882a593Smuzhiyun }
2776*4882a593Smuzhiyun
2777*4882a593Smuzhiyun if (!cfg->scan_request && !cfg->sched_scan_req) {
2778*4882a593Smuzhiyun /* No scans in progress */
2779*4882a593Smuzhiyun WL_MEM(("no scan in progress \n"));
2780*4882a593Smuzhiyun return NULL;
2781*4882a593Smuzhiyun }
2782*4882a593Smuzhiyun
2783*4882a593Smuzhiyun if (cfg->scan_request) {
2784*4882a593Smuzhiyun wdev = GET_SCAN_WDEV(cfg->scan_request);
2785*4882a593Smuzhiyun #ifdef WL_SCHED_SCAN
2786*4882a593Smuzhiyun } else if (cfg->sched_scan_req) {
2787*4882a593Smuzhiyun wdev = GET_SCHED_SCAN_WDEV(cfg->sched_scan_req);
2788*4882a593Smuzhiyun #endif /* WL_SCHED_SCAN */
2789*4882a593Smuzhiyun } else {
2790*4882a593Smuzhiyun WL_MEM(("no scan in progress \n"));
2791*4882a593Smuzhiyun }
2792*4882a593Smuzhiyun
2793*4882a593Smuzhiyun return wdev;
2794*4882a593Smuzhiyun }
2795*4882a593Smuzhiyun
_wl_cfgscan_cancel_scan(struct bcm_cfg80211 * cfg)2796*4882a593Smuzhiyun static void _wl_cfgscan_cancel_scan(struct bcm_cfg80211 *cfg)
2797*4882a593Smuzhiyun {
2798*4882a593Smuzhiyun struct wireless_dev *wdev = NULL;
2799*4882a593Smuzhiyun struct net_device *ndev = NULL;
2800*4882a593Smuzhiyun
2801*4882a593Smuzhiyun if (!cfg->scan_request && !cfg->sched_scan_req) {
2802*4882a593Smuzhiyun /* No scans in progress */
2803*4882a593Smuzhiyun WL_INFORM_MEM(("No scan in progress\n"));
2804*4882a593Smuzhiyun return;
2805*4882a593Smuzhiyun }
2806*4882a593Smuzhiyun
2807*4882a593Smuzhiyun wdev = wl_get_scan_wdev(cfg);
2808*4882a593Smuzhiyun if (!wdev) {
2809*4882a593Smuzhiyun WL_ERR(("No wdev present\n"));
2810*4882a593Smuzhiyun return;
2811*4882a593Smuzhiyun }
2812*4882a593Smuzhiyun
2813*4882a593Smuzhiyun ndev = wdev_to_wlc_ndev(wdev, cfg);
2814*4882a593Smuzhiyun
2815*4882a593Smuzhiyun /* Check if any scan in progress only then abort */
2816*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, SCANNING)) {
2817*4882a593Smuzhiyun wl_cfgscan_scan_abort(cfg);
2818*4882a593Smuzhiyun
2819*4882a593Smuzhiyun /* Indicate escan completion to upper layer */
2820*4882a593Smuzhiyun wl_notify_escan_complete(cfg, ndev, true);
2821*4882a593Smuzhiyun }
2822*4882a593Smuzhiyun WL_INFORM_MEM(("Scan aborted! \n"));
2823*4882a593Smuzhiyun }
2824*4882a593Smuzhiyun
2825*4882a593Smuzhiyun /* Wrapper function for cancel_scan with scan_sync mutex */
wl_cfgscan_cancel_scan(struct bcm_cfg80211 * cfg)2826*4882a593Smuzhiyun void wl_cfgscan_cancel_scan(struct bcm_cfg80211 *cfg)
2827*4882a593Smuzhiyun {
2828*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
2829*4882a593Smuzhiyun _wl_cfgscan_cancel_scan(cfg);
2830*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
2831*4882a593Smuzhiyun }
2832*4882a593Smuzhiyun
2833*4882a593Smuzhiyun /* Use wl_cfgscan_cancel_scan function for scan abort, as this would do a FW abort
2834*4882a593Smuzhiyun * followed by indication to upper layer, the current function wl_cfgscan_scan_abort, does
2835*4882a593Smuzhiyun * only FW abort.
2836*4882a593Smuzhiyun */
wl_cfgscan_scan_abort(struct bcm_cfg80211 * cfg)2837*4882a593Smuzhiyun void wl_cfgscan_scan_abort(struct bcm_cfg80211 *cfg)
2838*4882a593Smuzhiyun {
2839*4882a593Smuzhiyun void *params = NULL;
2840*4882a593Smuzhiyun s32 params_size = 0;
2841*4882a593Smuzhiyun s32 err = BCME_OK;
2842*4882a593Smuzhiyun struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
2843*4882a593Smuzhiyun u32 channel, channel_num;
2844*4882a593Smuzhiyun
2845*4882a593Smuzhiyun /* Abort scan params only need space for 1 channel and 0 ssids */
2846*4882a593Smuzhiyun if (cfg->scan_params_v2) {
2847*4882a593Smuzhiyun params_size = WL_SCAN_PARAMS_V2_FIXED_SIZE + (1 * sizeof(uint16));
2848*4882a593Smuzhiyun } else {
2849*4882a593Smuzhiyun params_size = WL_SCAN_PARAMS_FIXED_SIZE + (1 * sizeof(uint16));
2850*4882a593Smuzhiyun }
2851*4882a593Smuzhiyun
2852*4882a593Smuzhiyun params = MALLOCZ(cfg->osh, params_size);
2853*4882a593Smuzhiyun if (params == NULL) {
2854*4882a593Smuzhiyun WL_ERR(("mem alloc failed (%d bytes)\n", params_size));
2855*4882a593Smuzhiyun return;
2856*4882a593Smuzhiyun }
2857*4882a593Smuzhiyun
2858*4882a593Smuzhiyun /* Use magic value of channel=-1 to abort scan */
2859*4882a593Smuzhiyun channel = htodchanspec(-1);
2860*4882a593Smuzhiyun channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) |
2861*4882a593Smuzhiyun (1 & WL_SCAN_PARAMS_COUNT_MASK));
2862*4882a593Smuzhiyun if (cfg->scan_params_v2) {
2863*4882a593Smuzhiyun wl_scan_params_v2_t *params_v2 = (wl_scan_params_v2_t *)params;
2864*4882a593Smuzhiyun params_v2->channel_list[0] = channel;
2865*4882a593Smuzhiyun params_v2->channel_num = channel_num;
2866*4882a593Smuzhiyun params_v2->length = htod16(sizeof(wl_scan_params_v2_t));
2867*4882a593Smuzhiyun } else {
2868*4882a593Smuzhiyun wl_scan_params_t *params_v1 = (wl_scan_params_t *)params;
2869*4882a593Smuzhiyun params_v1->channel_list[0] = channel;
2870*4882a593Smuzhiyun params_v1->channel_num = channel_num;
2871*4882a593Smuzhiyun }
2872*4882a593Smuzhiyun #ifdef DHD_SEND_HANG_ESCAN_SYNCID_MISMATCH
2873*4882a593Smuzhiyun cfg->escan_info.prev_escan_aborted = TRUE;
2874*4882a593Smuzhiyun #endif /* DHD_SEND_HANG_ESCAN_SYNCID_MISMATCH */
2875*4882a593Smuzhiyun /* Do a scan abort to stop the driver's scan engine */
2876*4882a593Smuzhiyun err = wldev_ioctl_set(dev, WLC_SCAN, params, params_size);
2877*4882a593Smuzhiyun if (err < 0) {
2878*4882a593Smuzhiyun /* scan abort can fail if there is no outstanding scan */
2879*4882a593Smuzhiyun WL_ERR(("scan engine not aborted ret(%d)\n", err));
2880*4882a593Smuzhiyun }
2881*4882a593Smuzhiyun MFREE(cfg->osh, params, params_size);
2882*4882a593Smuzhiyun #ifdef WLTDLS
2883*4882a593Smuzhiyun if (cfg->tdls_mgmt_frame) {
2884*4882a593Smuzhiyun MFREE(cfg->osh, cfg->tdls_mgmt_frame, cfg->tdls_mgmt_frame_len);
2885*4882a593Smuzhiyun cfg->tdls_mgmt_frame = NULL;
2886*4882a593Smuzhiyun cfg->tdls_mgmt_frame_len = 0;
2887*4882a593Smuzhiyun }
2888*4882a593Smuzhiyun #endif /* WLTDLS */
2889*4882a593Smuzhiyun }
2890*4882a593Smuzhiyun
2891*4882a593Smuzhiyun static s32
wl_notify_escan_complete(struct bcm_cfg80211 * cfg,struct net_device * ndev,bool aborted)2892*4882a593Smuzhiyun wl_notify_escan_complete(struct bcm_cfg80211 *cfg,
2893*4882a593Smuzhiyun struct net_device *ndev, bool aborted)
2894*4882a593Smuzhiyun {
2895*4882a593Smuzhiyun s32 err = BCME_OK;
2896*4882a593Smuzhiyun unsigned long flags;
2897*4882a593Smuzhiyun struct net_device *dev;
2898*4882a593Smuzhiyun dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
2899*4882a593Smuzhiyun
2900*4882a593Smuzhiyun WL_DBG(("Enter \n"));
2901*4882a593Smuzhiyun BCM_REFERENCE(dhdp);
2902*4882a593Smuzhiyun
2903*4882a593Smuzhiyun if (!ndev) {
2904*4882a593Smuzhiyun WL_ERR(("ndev is null\n"));
2905*4882a593Smuzhiyun err = BCME_ERROR;
2906*4882a593Smuzhiyun goto out;
2907*4882a593Smuzhiyun }
2908*4882a593Smuzhiyun
2909*4882a593Smuzhiyun if (cfg->escan_info.ndev != ndev) {
2910*4882a593Smuzhiyun WL_ERR(("Outstanding scan req ndev not matching (%p:%p)\n",
2911*4882a593Smuzhiyun cfg->escan_info.ndev, ndev));
2912*4882a593Smuzhiyun err = BCME_ERROR;
2913*4882a593Smuzhiyun goto out;
2914*4882a593Smuzhiyun }
2915*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) && defined(SUPPORT_RANDOM_MAC_SCAN) && \
2916*4882a593Smuzhiyun (!defined(WL_USE_RANDOMIZED_SCAN))
2917*4882a593Smuzhiyun /* Disable scanmac if enabled */
2918*4882a593Smuzhiyun if (cfg->scanmac_enabled) {
2919*4882a593Smuzhiyun wl_cfg80211_scan_mac_disable(ndev);
2920*4882a593Smuzhiyun }
2921*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) && defined(SUPPORT_RANDOM_MAC_SCAN) */
2922*4882a593Smuzhiyun if (cfg->scan_request) {
2923*4882a593Smuzhiyun dev = bcmcfg_to_prmry_ndev(cfg);
2924*4882a593Smuzhiyun #if defined(WL_ENABLE_P2P_IF)
2925*4882a593Smuzhiyun if (cfg->scan_request->dev != cfg->p2p_net)
2926*4882a593Smuzhiyun dev = cfg->scan_request->dev;
2927*4882a593Smuzhiyun #elif defined(WL_CFG80211_P2P_DEV_IF)
2928*4882a593Smuzhiyun if (cfg->scan_request->wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
2929*4882a593Smuzhiyun dev = cfg->scan_request->wdev->netdev;
2930*4882a593Smuzhiyun #endif /* WL_ENABLE_P2P_IF */
2931*4882a593Smuzhiyun }
2932*4882a593Smuzhiyun else {
2933*4882a593Smuzhiyun WL_DBG(("cfg->scan_request is NULL. Internal scan scenario."
2934*4882a593Smuzhiyun "doing scan_abort for ndev %p primary %p",
2935*4882a593Smuzhiyun ndev, bcmcfg_to_prmry_ndev(cfg)));
2936*4882a593Smuzhiyun dev = ndev;
2937*4882a593Smuzhiyun }
2938*4882a593Smuzhiyun
2939*4882a593Smuzhiyun del_timer_sync(&cfg->scan_timeout);
2940*4882a593Smuzhiyun /* clear scan enq time on complete */
2941*4882a593Smuzhiyun CLR_TS(cfg, scan_enq);
2942*4882a593Smuzhiyun CLR_TS(cfg, scan_start);
2943*4882a593Smuzhiyun #if defined (ESCAN_RESULT_PATCH)
2944*4882a593Smuzhiyun if (likely(cfg->scan_request)) {
2945*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
2946*4882a593Smuzhiyun if (aborted && cfg->p2p && p2p_scan(cfg) &&
2947*4882a593Smuzhiyun (cfg->scan_request->flags & NL80211_SCAN_FLAG_FLUSH)) {
2948*4882a593Smuzhiyun WL_ERR(("scan list is changed"));
2949*4882a593Smuzhiyun cfg->bss_list = wl_escan_get_buf(cfg, !aborted);
2950*4882a593Smuzhiyun } else
2951*4882a593Smuzhiyun #endif
2952*4882a593Smuzhiyun cfg->bss_list = wl_escan_get_buf(cfg, aborted);
2953*4882a593Smuzhiyun
2954*4882a593Smuzhiyun wl_inform_bss(cfg);
2955*4882a593Smuzhiyun }
2956*4882a593Smuzhiyun #endif /* ESCAN_RESULT_PATCH */
2957*4882a593Smuzhiyun
2958*4882a593Smuzhiyun WL_CFG_DRV_LOCK(&cfg->cfgdrv_lock, flags);
2959*4882a593Smuzhiyun if (likely(cfg->scan_request)) {
2960*4882a593Smuzhiyun WL_INFORM_MEM(("[%s] Report scan done.\n", dev->name));
2961*4882a593Smuzhiyun /* scan_sync mutex is already held */
2962*4882a593Smuzhiyun _wl_notify_scan_done(cfg, aborted);
2963*4882a593Smuzhiyun cfg->scan_request = NULL;
2964*4882a593Smuzhiyun }
2965*4882a593Smuzhiyun if (p2p_is_on(cfg))
2966*4882a593Smuzhiyun wl_clr_p2p_status(cfg, SCANNING);
2967*4882a593Smuzhiyun wl_clr_drv_status(cfg, SCANNING, dev);
2968*4882a593Smuzhiyun CLR_TS(cfg, scan_start);
2969*4882a593Smuzhiyun WL_CFG_DRV_UNLOCK(&cfg->cfgdrv_lock, flags);
2970*4882a593Smuzhiyun
2971*4882a593Smuzhiyun #ifdef WL_SCHED_SCAN
2972*4882a593Smuzhiyun if (cfg->sched_scan_running && cfg->sched_scan_req) {
2973*4882a593Smuzhiyun struct wiphy *wiphy = cfg->sched_scan_req->wiphy;
2974*4882a593Smuzhiyun if (!aborted) {
2975*4882a593Smuzhiyun WL_INFORM_MEM(("[%s] Report sched scan done.\n", dev->name));
2976*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
2977*4882a593Smuzhiyun cfg80211_sched_scan_results(wiphy,
2978*4882a593Smuzhiyun cfg->sched_scan_req->reqid);
2979*4882a593Smuzhiyun #else
2980*4882a593Smuzhiyun cfg80211_sched_scan_results(wiphy);
2981*4882a593Smuzhiyun #endif /* LINUX_VER > 4.11 */
2982*4882a593Smuzhiyun }
2983*4882a593Smuzhiyun
2984*4882a593Smuzhiyun DBG_EVENT_LOG(dhdp, WIFI_EVENT_DRIVER_PNO_SCAN_COMPLETE);
2985*4882a593Smuzhiyun /* Mark target scan as done */
2986*4882a593Smuzhiyun cfg->sched_scan_running = FALSE;
2987*4882a593Smuzhiyun
2988*4882a593Smuzhiyun if (cfg->bss_list && (cfg->bss_list->count == 0)) {
2989*4882a593Smuzhiyun WL_INFORM_MEM(("bss list empty. report sched_scan_stop\n"));
2990*4882a593Smuzhiyun /* Indicated sched scan stopped so that user space
2991*4882a593Smuzhiyun * can do a full scan incase found match is empty.
2992*4882a593Smuzhiyun */
2993*4882a593Smuzhiyun CFG80211_SCHED_SCAN_STOPPED(wiphy, cfg->sched_scan_req);
2994*4882a593Smuzhiyun cfg->sched_scan_req = NULL;
2995*4882a593Smuzhiyun }
2996*4882a593Smuzhiyun }
2997*4882a593Smuzhiyun #endif /* WL_SCHED_SCAN */
2998*4882a593Smuzhiyun wake_up_interruptible(&dhdp->conf->event_complete);
2999*4882a593Smuzhiyun
3000*4882a593Smuzhiyun #if defined(BCMDONGLEHOST) && defined(OEM_ANDROID)
3001*4882a593Smuzhiyun DHD_OS_SCAN_WAKE_UNLOCK((dhd_pub_t *)(cfg->pub));
3002*4882a593Smuzhiyun DHD_ENABLE_RUNTIME_PM((dhd_pub_t *)(cfg->pub));
3003*4882a593Smuzhiyun #endif
3004*4882a593Smuzhiyun
3005*4882a593Smuzhiyun #ifdef WL_SDO
3006*4882a593Smuzhiyun if (wl_get_p2p_status(cfg, DISC_IN_PROGRESS) && !in_atomic()) {
3007*4882a593Smuzhiyun /* If it is in atomic, we probably have to wait till the
3008*4882a593Smuzhiyun * next event or find someother way of invoking this.
3009*4882a593Smuzhiyun */
3010*4882a593Smuzhiyun wl_cfg80211_resume_sdo(ndev, cfg);
3011*4882a593Smuzhiyun }
3012*4882a593Smuzhiyun #endif
3013*4882a593Smuzhiyun
3014*4882a593Smuzhiyun out:
3015*4882a593Smuzhiyun return err;
3016*4882a593Smuzhiyun }
3017*4882a593Smuzhiyun
3018*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0))
3019*4882a593Smuzhiyun void
wl_cfg80211_abort_scan(struct wiphy * wiphy,struct wireless_dev * wdev)3020*4882a593Smuzhiyun wl_cfg80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev)
3021*4882a593Smuzhiyun {
3022*4882a593Smuzhiyun struct bcm_cfg80211 *cfg;
3023*4882a593Smuzhiyun
3024*4882a593Smuzhiyun WL_DBG(("Enter wl_cfg80211_abort_scan\n"));
3025*4882a593Smuzhiyun cfg = wiphy_priv(wdev->wiphy);
3026*4882a593Smuzhiyun
3027*4882a593Smuzhiyun /* Check if any scan in progress only then abort */
3028*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, SCANNING)) {
3029*4882a593Smuzhiyun wl_cfgscan_scan_abort(cfg);
3030*4882a593Smuzhiyun /* Only scan abort is issued here. As per the expectation of abort_scan
3031*4882a593Smuzhiyun * the status of abort is needed to be communicated using cfg80211_scan_done call.
3032*4882a593Smuzhiyun * Here we just issue abort request and let the scan complete path to indicate
3033*4882a593Smuzhiyun * abort to cfg80211 layer.
3034*4882a593Smuzhiyun */
3035*4882a593Smuzhiyun WL_DBG(("wl_cfg80211_abort_scan: Scan abort issued to FW\n"));
3036*4882a593Smuzhiyun }
3037*4882a593Smuzhiyun }
3038*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)) */
3039*4882a593Smuzhiyun
3040*4882a593Smuzhiyun #if defined(OEM_ANDROID) && defined(DHCP_SCAN_SUPPRESS)
wl_cfg80211_scan_supp_timerfunc(ulong data)3041*4882a593Smuzhiyun static void wl_cfg80211_scan_supp_timerfunc(ulong data)
3042*4882a593Smuzhiyun {
3043*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)data;
3044*4882a593Smuzhiyun
3045*4882a593Smuzhiyun WL_DBG(("Enter \n"));
3046*4882a593Smuzhiyun schedule_work(&cfg->wlan_work);
3047*4882a593Smuzhiyun }
3048*4882a593Smuzhiyun
wl_cfg80211_scan_suppress(struct net_device * dev,int suppress)3049*4882a593Smuzhiyun int wl_cfg80211_scan_suppress(struct net_device *dev, int suppress)
3050*4882a593Smuzhiyun {
3051*4882a593Smuzhiyun int ret = 0;
3052*4882a593Smuzhiyun struct wireless_dev *wdev;
3053*4882a593Smuzhiyun struct bcm_cfg80211 *cfg;
3054*4882a593Smuzhiyun if (!dev || ((suppress != 0) && (suppress != 1))) {
3055*4882a593Smuzhiyun ret = -EINVAL;
3056*4882a593Smuzhiyun goto exit;
3057*4882a593Smuzhiyun }
3058*4882a593Smuzhiyun wdev = ndev_to_wdev(dev);
3059*4882a593Smuzhiyun if (!wdev) {
3060*4882a593Smuzhiyun ret = -EINVAL;
3061*4882a593Smuzhiyun goto exit;
3062*4882a593Smuzhiyun }
3063*4882a593Smuzhiyun cfg = (struct bcm_cfg80211 *)wiphy_priv(wdev->wiphy);
3064*4882a593Smuzhiyun if (!cfg) {
3065*4882a593Smuzhiyun ret = -EINVAL;
3066*4882a593Smuzhiyun goto exit;
3067*4882a593Smuzhiyun }
3068*4882a593Smuzhiyun
3069*4882a593Smuzhiyun if (suppress == cfg->scan_suppressed) {
3070*4882a593Smuzhiyun WL_DBG(("No change in scan_suppress state. Ignoring cmd..\n"));
3071*4882a593Smuzhiyun return 0;
3072*4882a593Smuzhiyun }
3073*4882a593Smuzhiyun
3074*4882a593Smuzhiyun del_timer_sync(&cfg->scan_supp_timer);
3075*4882a593Smuzhiyun
3076*4882a593Smuzhiyun if ((ret = wldev_ioctl_set(dev, WLC_SET_SCANSUPPRESS,
3077*4882a593Smuzhiyun &suppress, sizeof(int))) < 0) {
3078*4882a593Smuzhiyun WL_ERR(("Scan suppress setting failed ret:%d \n", ret));
3079*4882a593Smuzhiyun } else {
3080*4882a593Smuzhiyun WL_DBG(("Scan suppress %s \n", suppress ? "Enabled" : "Disabled"));
3081*4882a593Smuzhiyun cfg->scan_suppressed = suppress;
3082*4882a593Smuzhiyun }
3083*4882a593Smuzhiyun
3084*4882a593Smuzhiyun /* If scan_suppress is set, Start a timer to monitor it (just incase) */
3085*4882a593Smuzhiyun if (cfg->scan_suppressed) {
3086*4882a593Smuzhiyun if (ret) {
3087*4882a593Smuzhiyun WL_ERR(("Retry scan_suppress reset at a later time \n"));
3088*4882a593Smuzhiyun mod_timer(&cfg->scan_supp_timer,
3089*4882a593Smuzhiyun jiffies + msecs_to_jiffies(WL_SCAN_SUPPRESS_RETRY));
3090*4882a593Smuzhiyun } else {
3091*4882a593Smuzhiyun WL_DBG(("Start wlan_timer to clear of scan_suppress \n"));
3092*4882a593Smuzhiyun mod_timer(&cfg->scan_supp_timer,
3093*4882a593Smuzhiyun jiffies + msecs_to_jiffies(WL_SCAN_SUPPRESS_TIMEOUT));
3094*4882a593Smuzhiyun }
3095*4882a593Smuzhiyun }
3096*4882a593Smuzhiyun exit:
3097*4882a593Smuzhiyun return ret;
3098*4882a593Smuzhiyun }
3099*4882a593Smuzhiyun #endif /* DHCP_SCAN_SUPPRESS */
3100*4882a593Smuzhiyun
wl_cfg80211_scan_stop(struct bcm_cfg80211 * cfg,bcm_struct_cfgdev * cfgdev)3101*4882a593Smuzhiyun int wl_cfg80211_scan_stop(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev)
3102*4882a593Smuzhiyun {
3103*4882a593Smuzhiyun int ret = 0;
3104*4882a593Smuzhiyun
3105*4882a593Smuzhiyun WL_TRACE(("Enter\n"));
3106*4882a593Smuzhiyun
3107*4882a593Smuzhiyun if (!cfg || !cfgdev) {
3108*4882a593Smuzhiyun return -EINVAL;
3109*4882a593Smuzhiyun }
3110*4882a593Smuzhiyun
3111*4882a593Smuzhiyun /* cancel scan and notify scan status */
3112*4882a593Smuzhiyun wl_cfgscan_cancel_scan(cfg);
3113*4882a593Smuzhiyun
3114*4882a593Smuzhiyun return ret;
3115*4882a593Smuzhiyun }
3116*4882a593Smuzhiyun
3117*4882a593Smuzhiyun /* This API is just meant as a wrapper for cfg80211_scan_done
3118*4882a593Smuzhiyun * API. This doesn't do state mgmt. For cancelling scan,
3119*4882a593Smuzhiyun * please use wl_cfgscan_cancel_scan API.
3120*4882a593Smuzhiyun */
3121*4882a593Smuzhiyun static void
_wl_notify_scan_done(struct bcm_cfg80211 * cfg,bool aborted)3122*4882a593Smuzhiyun _wl_notify_scan_done(struct bcm_cfg80211 *cfg, bool aborted)
3123*4882a593Smuzhiyun {
3124*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
3125*4882a593Smuzhiyun struct cfg80211_scan_info info;
3126*4882a593Smuzhiyun #endif
3127*4882a593Smuzhiyun
3128*4882a593Smuzhiyun if (!cfg->scan_request) {
3129*4882a593Smuzhiyun return;
3130*4882a593Smuzhiyun }
3131*4882a593Smuzhiyun
3132*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
3133*4882a593Smuzhiyun memset_s(&info, sizeof(struct cfg80211_scan_info), 0, sizeof(struct cfg80211_scan_info));
3134*4882a593Smuzhiyun info.aborted = aborted;
3135*4882a593Smuzhiyun cfg80211_scan_done(cfg->scan_request, &info);
3136*4882a593Smuzhiyun #else
3137*4882a593Smuzhiyun cfg80211_scan_done(cfg->scan_request, aborted);
3138*4882a593Smuzhiyun #endif
3139*4882a593Smuzhiyun cfg->scan_request = NULL;
3140*4882a593Smuzhiyun }
3141*4882a593Smuzhiyun
3142*4882a593Smuzhiyun #ifdef WL_DRV_AVOID_SCANCACHE
wl_p2p_find_peer_channel(struct bcm_cfg80211 * cfg,s32 status,wl_bss_info_t * bi,u32 bi_length)3143*4882a593Smuzhiyun static u32 wl_p2p_find_peer_channel(struct bcm_cfg80211 *cfg, s32 status, wl_bss_info_t *bi,
3144*4882a593Smuzhiyun u32 bi_length)
3145*4882a593Smuzhiyun {
3146*4882a593Smuzhiyun u32 ret;
3147*4882a593Smuzhiyun u8 *p2p_dev_addr = NULL;
3148*4882a593Smuzhiyun
3149*4882a593Smuzhiyun ret = wl_get_drv_status_all(cfg, FINDING_COMMON_CHANNEL);
3150*4882a593Smuzhiyun if (!ret) {
3151*4882a593Smuzhiyun return ret;
3152*4882a593Smuzhiyun }
3153*4882a593Smuzhiyun if (status == WLC_E_STATUS_PARTIAL) {
3154*4882a593Smuzhiyun p2p_dev_addr = wl_cfgp2p_retreive_p2p_dev_addr(bi, bi_length);
3155*4882a593Smuzhiyun if (p2p_dev_addr && !memcmp(p2p_dev_addr,
3156*4882a593Smuzhiyun cfg->afx_hdl->tx_dst_addr.octet, ETHER_ADDR_LEN)) {
3157*4882a593Smuzhiyun s32 channel = wf_chspec_ctlchan(
3158*4882a593Smuzhiyun wl_chspec_driver_to_host(bi->chanspec));
3159*4882a593Smuzhiyun
3160*4882a593Smuzhiyun if ((channel > MAXCHANNEL) || (channel <= 0)) {
3161*4882a593Smuzhiyun channel = WL_INVALID;
3162*4882a593Smuzhiyun } else {
3163*4882a593Smuzhiyun WL_ERR(("ACTION FRAME SCAN : Peer " MACDBG " found,"
3164*4882a593Smuzhiyun " channel : %d\n",
3165*4882a593Smuzhiyun MAC2STRDBG(cfg->afx_hdl->tx_dst_addr.octet),
3166*4882a593Smuzhiyun channel));
3167*4882a593Smuzhiyun }
3168*4882a593Smuzhiyun wl_clr_p2p_status(cfg, SCANNING);
3169*4882a593Smuzhiyun cfg->afx_hdl->peer_chan = channel;
3170*4882a593Smuzhiyun complete(&cfg->act_frm_scan);
3171*4882a593Smuzhiyun }
3172*4882a593Smuzhiyun } else {
3173*4882a593Smuzhiyun WL_INFORM_MEM(("ACTION FRAME SCAN DONE\n"));
3174*4882a593Smuzhiyun wl_clr_p2p_status(cfg, SCANNING);
3175*4882a593Smuzhiyun wl_clr_drv_status(cfg, SCANNING, cfg->afx_hdl->dev);
3176*4882a593Smuzhiyun if (cfg->afx_hdl->peer_chan == WL_INVALID)
3177*4882a593Smuzhiyun complete(&cfg->act_frm_scan);
3178*4882a593Smuzhiyun }
3179*4882a593Smuzhiyun
3180*4882a593Smuzhiyun return ret;
3181*4882a593Smuzhiyun }
3182*4882a593Smuzhiyun
wl_escan_without_scan_cache(struct bcm_cfg80211 * cfg,wl_escan_result_t * escan_result,struct net_device * ndev,const wl_event_msg_t * e,s32 status)3183*4882a593Smuzhiyun static s32 wl_escan_without_scan_cache(struct bcm_cfg80211 *cfg, wl_escan_result_t *escan_result,
3184*4882a593Smuzhiyun struct net_device *ndev, const wl_event_msg_t *e, s32 status)
3185*4882a593Smuzhiyun {
3186*4882a593Smuzhiyun s32 err = BCME_OK;
3187*4882a593Smuzhiyun wl_bss_info_t *bi;
3188*4882a593Smuzhiyun u32 bi_length;
3189*4882a593Smuzhiyun bool aborted = false;
3190*4882a593Smuzhiyun bool fw_abort = false;
3191*4882a593Smuzhiyun bool notify_escan_complete = false;
3192*4882a593Smuzhiyun
3193*4882a593Smuzhiyun if (wl_escan_check_sync_id(cfg, status, escan_result->sync_id,
3194*4882a593Smuzhiyun cfg->escan_info.cur_sync_id) < 0) {
3195*4882a593Smuzhiyun goto exit;
3196*4882a593Smuzhiyun }
3197*4882a593Smuzhiyun
3198*4882a593Smuzhiyun if (!(status == WLC_E_STATUS_TIMEOUT) || !(status == WLC_E_STATUS_PARTIAL)) {
3199*4882a593Smuzhiyun cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3200*4882a593Smuzhiyun }
3201*4882a593Smuzhiyun
3202*4882a593Smuzhiyun if ((likely(cfg->scan_request)) || (cfg->sched_scan_running)) {
3203*4882a593Smuzhiyun notify_escan_complete = true;
3204*4882a593Smuzhiyun }
3205*4882a593Smuzhiyun
3206*4882a593Smuzhiyun if (status == WLC_E_STATUS_PARTIAL) {
3207*4882a593Smuzhiyun WL_DBG(("WLC_E_STATUS_PARTIAL \n"));
3208*4882a593Smuzhiyun DBG_EVENT_LOG((dhd_pub_t *)cfg->pub, WIFI_EVENT_DRIVER_SCAN_RESULT_FOUND);
3209*4882a593Smuzhiyun if ((!escan_result) || (dtoh16(escan_result->bss_count) != 1)) {
3210*4882a593Smuzhiyun WL_ERR(("Invalid escan result (NULL pointer) or invalid bss_count\n"));
3211*4882a593Smuzhiyun goto exit;
3212*4882a593Smuzhiyun }
3213*4882a593Smuzhiyun
3214*4882a593Smuzhiyun bi = escan_result->bss_info;
3215*4882a593Smuzhiyun bi_length = dtoh32(bi->length);
3216*4882a593Smuzhiyun if ((!bi) ||
3217*4882a593Smuzhiyun (bi_length != (dtoh32(escan_result->buflen) - WL_ESCAN_RESULTS_FIXED_SIZE))) {
3218*4882a593Smuzhiyun WL_ERR(("Invalid escan bss info (NULL pointer)"
3219*4882a593Smuzhiyun "or invalid bss_info length\n"));
3220*4882a593Smuzhiyun goto exit;
3221*4882a593Smuzhiyun }
3222*4882a593Smuzhiyun
3223*4882a593Smuzhiyun if (!(bcmcfg_to_wiphy(cfg)->interface_modes & BIT(NL80211_IFTYPE_ADHOC))) {
3224*4882a593Smuzhiyun if (dtoh16(bi->capability) & DOT11_CAP_IBSS) {
3225*4882a593Smuzhiyun WL_DBG(("Ignoring IBSS result\n"));
3226*4882a593Smuzhiyun goto exit;
3227*4882a593Smuzhiyun }
3228*4882a593Smuzhiyun }
3229*4882a593Smuzhiyun
3230*4882a593Smuzhiyun if (wl_p2p_find_peer_channel(cfg, status, bi, bi_length)) {
3231*4882a593Smuzhiyun goto exit;
3232*4882a593Smuzhiyun } else {
3233*4882a593Smuzhiyun if (scan_req_match(cfg)) {
3234*4882a593Smuzhiyun /* p2p scan && allow only probe response */
3235*4882a593Smuzhiyun if ((cfg->p2p->search_state != WL_P2P_DISC_ST_SCAN) &&
3236*4882a593Smuzhiyun (bi->flags & WL_BSS_FLAGS_FROM_BEACON))
3237*4882a593Smuzhiyun goto exit;
3238*4882a593Smuzhiyun }
3239*4882a593Smuzhiyun #ifdef ROAM_CHANNEL_CACHE
3240*4882a593Smuzhiyun add_roam_cache(cfg, bi);
3241*4882a593Smuzhiyun #endif /* ROAM_CHANNEL_CACHE */
3242*4882a593Smuzhiyun err = wl_inform_single_bss(cfg, bi, false);
3243*4882a593Smuzhiyun #ifdef ROAM_CHANNEL_CACHE
3244*4882a593Smuzhiyun /* print_roam_cache(); */
3245*4882a593Smuzhiyun update_roam_cache(cfg, ioctl_version);
3246*4882a593Smuzhiyun #endif /* ROAM_CHANNEL_CACHE */
3247*4882a593Smuzhiyun
3248*4882a593Smuzhiyun /*
3249*4882a593Smuzhiyun * !Broadcast && number of ssid = 1 && number of channels =1
3250*4882a593Smuzhiyun * means specific scan to association
3251*4882a593Smuzhiyun */
3252*4882a593Smuzhiyun if (wl_cfgp2p_is_p2p_specific_scan(cfg->scan_request)) {
3253*4882a593Smuzhiyun WL_ERR(("P2P assoc scan fast aborted.\n"));
3254*4882a593Smuzhiyun aborted = false;
3255*4882a593Smuzhiyun fw_abort = true;
3256*4882a593Smuzhiyun }
3257*4882a593Smuzhiyun /* Directly exit from function here and
3258*4882a593Smuzhiyun * avoid sending notify completion to cfg80211
3259*4882a593Smuzhiyun */
3260*4882a593Smuzhiyun goto exit;
3261*4882a593Smuzhiyun }
3262*4882a593Smuzhiyun } else if (status == WLC_E_STATUS_SUCCESS) {
3263*4882a593Smuzhiyun if (wl_p2p_find_peer_channel(cfg, status, NULL, 0)) {
3264*4882a593Smuzhiyun goto exit;
3265*4882a593Smuzhiyun }
3266*4882a593Smuzhiyun WL_INFORM_MEM(("ESCAN COMPLETED\n"));
3267*4882a593Smuzhiyun DBG_EVENT_LOG((dhd_pub_t *)cfg->pub, WIFI_EVENT_DRIVER_SCAN_COMPLETE);
3268*4882a593Smuzhiyun
3269*4882a593Smuzhiyun /* Update escan complete status */
3270*4882a593Smuzhiyun aborted = false;
3271*4882a593Smuzhiyun fw_abort = false;
3272*4882a593Smuzhiyun
3273*4882a593Smuzhiyun #ifdef CUSTOMER_HW4_DEBUG
3274*4882a593Smuzhiyun if (wl_scan_timeout_dbg_enabled)
3275*4882a593Smuzhiyun wl_scan_timeout_dbg_clear();
3276*4882a593Smuzhiyun #endif /* CUSTOMER_HW4_DEBUG */
3277*4882a593Smuzhiyun } else if ((status == WLC_E_STATUS_ABORT) || (status == WLC_E_STATUS_NEWSCAN) ||
3278*4882a593Smuzhiyun (status == WLC_E_STATUS_11HQUIET) || (status == WLC_E_STATUS_CS_ABORT) ||
3279*4882a593Smuzhiyun (status == WLC_E_STATUS_NEWASSOC)) {
3280*4882a593Smuzhiyun /* Handle all cases of scan abort */
3281*4882a593Smuzhiyun
3282*4882a593Smuzhiyun WL_DBG(("ESCAN ABORT reason: %d\n", status));
3283*4882a593Smuzhiyun if (wl_p2p_find_peer_channel(cfg, status, NULL, 0)) {
3284*4882a593Smuzhiyun goto exit;
3285*4882a593Smuzhiyun }
3286*4882a593Smuzhiyun WL_INFORM_MEM(("ESCAN ABORTED\n"));
3287*4882a593Smuzhiyun
3288*4882a593Smuzhiyun /* Update escan complete status */
3289*4882a593Smuzhiyun aborted = true;
3290*4882a593Smuzhiyun fw_abort = false;
3291*4882a593Smuzhiyun
3292*4882a593Smuzhiyun } else if (status == WLC_E_STATUS_TIMEOUT) {
3293*4882a593Smuzhiyun WL_ERR(("WLC_E_STATUS_TIMEOUT : scan_request[%p]\n", cfg->scan_request));
3294*4882a593Smuzhiyun WL_ERR(("reason[0x%x]\n", e->reason));
3295*4882a593Smuzhiyun if (e->reason == 0xFFFFFFFF) {
3296*4882a593Smuzhiyun /* Update escan complete status */
3297*4882a593Smuzhiyun aborted = true;
3298*4882a593Smuzhiyun fw_abort = true;
3299*4882a593Smuzhiyun }
3300*4882a593Smuzhiyun } else {
3301*4882a593Smuzhiyun WL_ERR(("unexpected Escan Event %d : abort\n", status));
3302*4882a593Smuzhiyun
3303*4882a593Smuzhiyun if (wl_p2p_find_peer_channel(cfg, status, NULL, 0)) {
3304*4882a593Smuzhiyun goto exit;
3305*4882a593Smuzhiyun }
3306*4882a593Smuzhiyun /* Update escan complete status */
3307*4882a593Smuzhiyun aborted = true;
3308*4882a593Smuzhiyun fw_abort = false;
3309*4882a593Smuzhiyun }
3310*4882a593Smuzhiyun
3311*4882a593Smuzhiyun /* Notify escan complete status */
3312*4882a593Smuzhiyun if (notify_escan_complete) {
3313*4882a593Smuzhiyun if (fw_abort == true) {
3314*4882a593Smuzhiyun wl_cfgscan_cancel_scan(cfg);
3315*4882a593Smuzhiyun } else {
3316*4882a593Smuzhiyun wl_notify_escan_complete(cfg, ndev, aborted);
3317*4882a593Smuzhiyun }
3318*4882a593Smuzhiyun }
3319*4882a593Smuzhiyun
3320*4882a593Smuzhiyun exit:
3321*4882a593Smuzhiyun return err;
3322*4882a593Smuzhiyun
3323*4882a593Smuzhiyun }
3324*4882a593Smuzhiyun #endif /* WL_DRV_AVOID_SCANCACHE */
3325*4882a593Smuzhiyun
3326*4882a593Smuzhiyun s32
wl_notify_scan_status(struct bcm_cfg80211 * cfg,bcm_struct_cfgdev * cfgdev,const wl_event_msg_t * e,void * data)3327*4882a593Smuzhiyun wl_notify_scan_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
3328*4882a593Smuzhiyun const wl_event_msg_t *e, void *data)
3329*4882a593Smuzhiyun {
3330*4882a593Smuzhiyun struct channel_info channel_inform;
3331*4882a593Smuzhiyun wl_scan_results_t *bss_list;
3332*4882a593Smuzhiyun struct net_device *ndev = NULL;
3333*4882a593Smuzhiyun u32 len = WL_SCAN_BUF_MAX;
3334*4882a593Smuzhiyun s32 err = 0;
3335*4882a593Smuzhiyun unsigned long flags;
3336*4882a593Smuzhiyun
3337*4882a593Smuzhiyun WL_DBG(("Enter \n"));
3338*4882a593Smuzhiyun if (!wl_get_drv_status(cfg, SCANNING, ndev)) {
3339*4882a593Smuzhiyun WL_DBG(("scan is not ready \n"));
3340*4882a593Smuzhiyun return err;
3341*4882a593Smuzhiyun }
3342*4882a593Smuzhiyun ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);
3343*4882a593Smuzhiyun
3344*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
3345*4882a593Smuzhiyun wl_clr_drv_status(cfg, SCANNING, ndev);
3346*4882a593Smuzhiyun bzero(&channel_inform, sizeof(channel_inform));
3347*4882a593Smuzhiyun err = wldev_ioctl_get(ndev, WLC_GET_CHANNEL, &channel_inform,
3348*4882a593Smuzhiyun sizeof(channel_inform));
3349*4882a593Smuzhiyun if (unlikely(err)) {
3350*4882a593Smuzhiyun WL_ERR(("scan busy (%d)\n", err));
3351*4882a593Smuzhiyun goto scan_done_out;
3352*4882a593Smuzhiyun }
3353*4882a593Smuzhiyun channel_inform.scan_channel = dtoh32(channel_inform.scan_channel);
3354*4882a593Smuzhiyun if (unlikely(channel_inform.scan_channel)) {
3355*4882a593Smuzhiyun
3356*4882a593Smuzhiyun WL_DBG(("channel_inform.scan_channel (%d)\n",
3357*4882a593Smuzhiyun channel_inform.scan_channel));
3358*4882a593Smuzhiyun }
3359*4882a593Smuzhiyun cfg->bss_list = cfg->scan_results;
3360*4882a593Smuzhiyun bss_list = cfg->bss_list;
3361*4882a593Smuzhiyun bzero(bss_list, len);
3362*4882a593Smuzhiyun bss_list->buflen = htod32(len);
3363*4882a593Smuzhiyun err = wldev_ioctl_get(ndev, WLC_SCAN_RESULTS, bss_list, len);
3364*4882a593Smuzhiyun if (unlikely(err) && unlikely(!cfg->scan_suppressed)) {
3365*4882a593Smuzhiyun WL_ERR(("%s Scan_results error (%d)\n", ndev->name, err));
3366*4882a593Smuzhiyun err = -EINVAL;
3367*4882a593Smuzhiyun goto scan_done_out;
3368*4882a593Smuzhiyun }
3369*4882a593Smuzhiyun bss_list->buflen = dtoh32(bss_list->buflen);
3370*4882a593Smuzhiyun bss_list->version = dtoh32(bss_list->version);
3371*4882a593Smuzhiyun bss_list->count = dtoh32(bss_list->count);
3372*4882a593Smuzhiyun
3373*4882a593Smuzhiyun err = wl_inform_bss(cfg);
3374*4882a593Smuzhiyun
3375*4882a593Smuzhiyun scan_done_out:
3376*4882a593Smuzhiyun del_timer_sync(&cfg->scan_timeout);
3377*4882a593Smuzhiyun WL_CFG_DRV_LOCK(&cfg->cfgdrv_lock, flags);
3378*4882a593Smuzhiyun if (cfg->scan_request) {
3379*4882a593Smuzhiyun _wl_notify_scan_done(cfg, false);
3380*4882a593Smuzhiyun cfg->scan_request = NULL;
3381*4882a593Smuzhiyun }
3382*4882a593Smuzhiyun WL_CFG_DRV_UNLOCK(&cfg->cfgdrv_lock, flags);
3383*4882a593Smuzhiyun WL_DBG(("cfg80211_scan_done\n"));
3384*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
3385*4882a593Smuzhiyun return err;
3386*4882a593Smuzhiyun }
3387*4882a593Smuzhiyun
wl_notify_scan_done(struct bcm_cfg80211 * cfg,bool aborted)3388*4882a593Smuzhiyun void wl_notify_scan_done(struct bcm_cfg80211 *cfg, bool aborted)
3389*4882a593Smuzhiyun {
3390*4882a593Smuzhiyun #if defined(CONFIG_TIZEN)
3391*4882a593Smuzhiyun struct net_device *ndev = NULL;
3392*4882a593Smuzhiyun #endif /* CONFIG_TIZEN */
3393*4882a593Smuzhiyun
3394*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
3395*4882a593Smuzhiyun struct cfg80211_scan_info info;
3396*4882a593Smuzhiyun
3397*4882a593Smuzhiyun bzero(&info, sizeof(struct cfg80211_scan_info));
3398*4882a593Smuzhiyun info.aborted = aborted;
3399*4882a593Smuzhiyun cfg80211_scan_done(cfg->scan_request, &info);
3400*4882a593Smuzhiyun #else
3401*4882a593Smuzhiyun cfg80211_scan_done(cfg->scan_request, aborted);
3402*4882a593Smuzhiyun #endif
3403*4882a593Smuzhiyun
3404*4882a593Smuzhiyun #if defined(CONFIG_TIZEN)
3405*4882a593Smuzhiyun ndev = bcmcfg_to_prmry_ndev(cfg);
3406*4882a593Smuzhiyun if (aborted)
3407*4882a593Smuzhiyun net_stat_tizen_update_wifi(ndev, WIFISTAT_SCAN_ABORT);
3408*4882a593Smuzhiyun else
3409*4882a593Smuzhiyun net_stat_tizen_update_wifi(ndev, WIFISTAT_SCAN_DONE);
3410*4882a593Smuzhiyun #endif /* CONFIG_TIZEN */
3411*4882a593Smuzhiyun }
3412*4882a593Smuzhiyun
3413*4882a593Smuzhiyun #if defined(SUPPORT_RANDOM_MAC_SCAN)
3414*4882a593Smuzhiyun int
wl_cfg80211_set_random_mac(struct net_device * dev,bool enable)3415*4882a593Smuzhiyun wl_cfg80211_set_random_mac(struct net_device *dev, bool enable)
3416*4882a593Smuzhiyun {
3417*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
3418*4882a593Smuzhiyun int ret;
3419*4882a593Smuzhiyun
3420*4882a593Smuzhiyun if (cfg->random_mac_enabled == enable) {
3421*4882a593Smuzhiyun WL_ERR(("Random MAC already %s\n", enable ? "Enabled" : "Disabled"));
3422*4882a593Smuzhiyun return BCME_OK;
3423*4882a593Smuzhiyun }
3424*4882a593Smuzhiyun
3425*4882a593Smuzhiyun if (enable) {
3426*4882a593Smuzhiyun ret = wl_cfg80211_random_mac_enable(dev);
3427*4882a593Smuzhiyun } else {
3428*4882a593Smuzhiyun ret = wl_cfg80211_random_mac_disable(dev);
3429*4882a593Smuzhiyun }
3430*4882a593Smuzhiyun
3431*4882a593Smuzhiyun if (!ret) {
3432*4882a593Smuzhiyun cfg->random_mac_enabled = enable;
3433*4882a593Smuzhiyun }
3434*4882a593Smuzhiyun
3435*4882a593Smuzhiyun return ret;
3436*4882a593Smuzhiyun }
3437*4882a593Smuzhiyun
3438*4882a593Smuzhiyun int
wl_cfg80211_random_mac_enable(struct net_device * dev)3439*4882a593Smuzhiyun wl_cfg80211_random_mac_enable(struct net_device *dev)
3440*4882a593Smuzhiyun {
3441*4882a593Smuzhiyun u8 random_mac[ETH_ALEN] = {0, };
3442*4882a593Smuzhiyun u8 rand_bytes[3] = {0, };
3443*4882a593Smuzhiyun s32 err = BCME_ERROR;
3444*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
3445*4882a593Smuzhiyun #if !defined(LEGACY_RANDOM_MAC)
3446*4882a593Smuzhiyun uint8 buffer[WLC_IOCTL_SMLEN] = {0, };
3447*4882a593Smuzhiyun wl_scanmac_t *sm = NULL;
3448*4882a593Smuzhiyun int len = 0;
3449*4882a593Smuzhiyun wl_scanmac_enable_t *sm_enable = NULL;
3450*4882a593Smuzhiyun wl_scanmac_config_t *sm_config = NULL;
3451*4882a593Smuzhiyun #endif /* !LEGACY_RANDOM_MAC */
3452*4882a593Smuzhiyun
3453*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, CONNECTED) || wl_get_drv_status_all(cfg, CONNECTING) ||
3454*4882a593Smuzhiyun wl_get_drv_status_all(cfg, AP_CREATED) || wl_get_drv_status_all(cfg, AP_CREATING)) {
3455*4882a593Smuzhiyun WL_ERR(("fail to Set random mac, current state is wrong\n"));
3456*4882a593Smuzhiyun return err;
3457*4882a593Smuzhiyun }
3458*4882a593Smuzhiyun
3459*4882a593Smuzhiyun (void)memcpy_s(random_mac, ETH_ALEN, bcmcfg_to_prmry_ndev(cfg)->dev_addr, ETH_ALEN);
3460*4882a593Smuzhiyun get_random_bytes(&rand_bytes, sizeof(rand_bytes));
3461*4882a593Smuzhiyun
3462*4882a593Smuzhiyun if (rand_bytes[2] == 0x0 || rand_bytes[2] == 0xff) {
3463*4882a593Smuzhiyun rand_bytes[2] = 0xf0;
3464*4882a593Smuzhiyun }
3465*4882a593Smuzhiyun
3466*4882a593Smuzhiyun #if defined(LEGACY_RANDOM_MAC)
3467*4882a593Smuzhiyun /* of the six bytes of random_mac the bytes 3, 4, 5 are copied with contents of rand_bytes
3468*4882a593Smuzhiyun * So while copying 3 bytes of content no overflow would be seen. Hence returning void.
3469*4882a593Smuzhiyun */
3470*4882a593Smuzhiyun (void)memcpy_s(&random_mac[3], (sizeof(u8) * 3), rand_bytes, sizeof(rand_bytes));
3471*4882a593Smuzhiyun
3472*4882a593Smuzhiyun err = wldev_iovar_setbuf_bsscfg(bcmcfg_to_prmry_ndev(cfg), "cur_etheraddr",
3473*4882a593Smuzhiyun random_mac, ETH_ALEN, cfg->ioctl_buf, WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync);
3474*4882a593Smuzhiyun
3475*4882a593Smuzhiyun if (err != BCME_OK) {
3476*4882a593Smuzhiyun WL_ERR(("failed to set random generate MAC address\n"));
3477*4882a593Smuzhiyun } else {
3478*4882a593Smuzhiyun WL_ERR(("set mac " MACDBG " to " MACDBG "\n",
3479*4882a593Smuzhiyun MAC2STRDBG((const u8 *)bcmcfg_to_prmry_ndev(cfg)->dev_addr),
3480*4882a593Smuzhiyun MAC2STRDBG((const u8 *)&random_mac)));
3481*4882a593Smuzhiyun WL_ERR(("random MAC enable done"));
3482*4882a593Smuzhiyun }
3483*4882a593Smuzhiyun #else
3484*4882a593Smuzhiyun /* Enable scan mac */
3485*4882a593Smuzhiyun sm = (wl_scanmac_t *)buffer;
3486*4882a593Smuzhiyun sm_enable = (wl_scanmac_enable_t *)sm->data;
3487*4882a593Smuzhiyun sm->len = sizeof(*sm_enable);
3488*4882a593Smuzhiyun sm_enable->enable = 1;
3489*4882a593Smuzhiyun len = OFFSETOF(wl_scanmac_t, data) + sm->len;
3490*4882a593Smuzhiyun sm->subcmd_id = WL_SCANMAC_SUBCMD_ENABLE;
3491*4882a593Smuzhiyun
3492*4882a593Smuzhiyun err = wldev_iovar_setbuf_bsscfg(dev, "scanmac",
3493*4882a593Smuzhiyun sm, len, cfg->ioctl_buf, WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync);
3494*4882a593Smuzhiyun
3495*4882a593Smuzhiyun /* For older chip which which does not have scanmac support can still use
3496*4882a593Smuzhiyun * cur_etheraddr to set the randmac. rand_mask and rand_mac comes from upper
3497*4882a593Smuzhiyun * cfg80211 layer. If rand_mask and rand_mac is not passed then fallback
3498*4882a593Smuzhiyun * to default cur_etheraddr and default mask.
3499*4882a593Smuzhiyun */
3500*4882a593Smuzhiyun if (err == BCME_UNSUPPORTED) {
3501*4882a593Smuzhiyun /* In case of host based legacy randomization, random address is
3502*4882a593Smuzhiyun * generated by mixing 3 bytes of cur_etheraddr and 3 bytes of
3503*4882a593Smuzhiyun * random bytes generated.In that case rand_mask is nothing but
3504*4882a593Smuzhiyun * random bytes.
3505*4882a593Smuzhiyun */
3506*4882a593Smuzhiyun (void)memcpy_s(&random_mac[3], (sizeof(u8) * 3), rand_bytes, sizeof(rand_bytes));
3507*4882a593Smuzhiyun err = wldev_iovar_setbuf_bsscfg(bcmcfg_to_prmry_ndev(cfg), "cur_etheraddr",
3508*4882a593Smuzhiyun random_mac, ETH_ALEN, cfg->ioctl_buf,
3509*4882a593Smuzhiyun WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync);
3510*4882a593Smuzhiyun if (err != BCME_OK) {
3511*4882a593Smuzhiyun WL_ERR(("failed to set random generate MAC address\n"));
3512*4882a593Smuzhiyun } else {
3513*4882a593Smuzhiyun WL_ERR(("set mac " MACDBG " to " MACDBG "\n",
3514*4882a593Smuzhiyun MAC2STRDBG((const u8 *)bcmcfg_to_prmry_ndev(cfg)->dev_addr),
3515*4882a593Smuzhiyun MAC2STRDBG((const u8 *)&random_mac)));
3516*4882a593Smuzhiyun WL_ERR(("random MAC enable done using legacy randmac"));
3517*4882a593Smuzhiyun }
3518*4882a593Smuzhiyun } else if (err == BCME_OK) {
3519*4882a593Smuzhiyun /* Configure scanmac */
3520*4882a593Smuzhiyun (void)memset_s(buffer, sizeof(buffer), 0x0, sizeof(buffer));
3521*4882a593Smuzhiyun sm_config = (wl_scanmac_config_t *)sm->data;
3522*4882a593Smuzhiyun sm->len = sizeof(*sm_config);
3523*4882a593Smuzhiyun sm->subcmd_id = WL_SCANMAC_SUBCMD_CONFIG;
3524*4882a593Smuzhiyun sm_config->scan_bitmap = WL_SCANMAC_SCAN_UNASSOC;
3525*4882a593Smuzhiyun
3526*4882a593Smuzhiyun /* Set randomize mac address recv from upper layer */
3527*4882a593Smuzhiyun (void)memcpy_s(&sm_config->mac.octet, ETH_ALEN, random_mac, ETH_ALEN);
3528*4882a593Smuzhiyun
3529*4882a593Smuzhiyun /* Set randomize mask recv from upper layer */
3530*4882a593Smuzhiyun
3531*4882a593Smuzhiyun /* Currently in samsung case, upper layer does not provide
3532*4882a593Smuzhiyun * variable randmask and its using fixed 3 byte randomization
3533*4882a593Smuzhiyun */
3534*4882a593Smuzhiyun (void)memset_s(&sm_config->random_mask.octet, ETH_ALEN, 0x0, ETH_ALEN);
3535*4882a593Smuzhiyun /* Memsetting the remaining octets 3, 4, 5. So remaining dest length is 3 */
3536*4882a593Smuzhiyun (void)memset_s(&sm_config->random_mask.octet[3], 3, 0xFF, 3);
3537*4882a593Smuzhiyun
3538*4882a593Smuzhiyun WL_DBG(("recv random mac addr " MACDBG " recv rand mask" MACDBG "\n",
3539*4882a593Smuzhiyun MAC2STRDBG((const u8 *)&sm_config->mac.octet),
3540*4882a593Smuzhiyun MAC2STRDBG((const u8 *)&sm_config->random_mask)));
3541*4882a593Smuzhiyun
3542*4882a593Smuzhiyun len = OFFSETOF(wl_scanmac_t, data) + sm->len;
3543*4882a593Smuzhiyun err = wldev_iovar_setbuf_bsscfg(dev, "scanmac",
3544*4882a593Smuzhiyun sm, len, cfg->ioctl_buf, WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync);
3545*4882a593Smuzhiyun
3546*4882a593Smuzhiyun if (err != BCME_OK) {
3547*4882a593Smuzhiyun WL_ERR(("failed scanmac configuration\n"));
3548*4882a593Smuzhiyun
3549*4882a593Smuzhiyun /* Disable scan mac for clean-up */
3550*4882a593Smuzhiyun wl_cfg80211_random_mac_disable(dev);
3551*4882a593Smuzhiyun return err;
3552*4882a593Smuzhiyun }
3553*4882a593Smuzhiyun WL_DBG(("random MAC enable done using scanmac"));
3554*4882a593Smuzhiyun } else {
3555*4882a593Smuzhiyun WL_ERR(("failed to enable scanmac, err=%d\n", err));
3556*4882a593Smuzhiyun }
3557*4882a593Smuzhiyun #endif /* LEGACY_RANDOM_MAC */
3558*4882a593Smuzhiyun
3559*4882a593Smuzhiyun return err;
3560*4882a593Smuzhiyun }
3561*4882a593Smuzhiyun
3562*4882a593Smuzhiyun int
wl_cfg80211_random_mac_disable(struct net_device * dev)3563*4882a593Smuzhiyun wl_cfg80211_random_mac_disable(struct net_device *dev)
3564*4882a593Smuzhiyun {
3565*4882a593Smuzhiyun s32 err = BCME_ERROR;
3566*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
3567*4882a593Smuzhiyun #if !defined(LEGACY_RANDOM_MAC)
3568*4882a593Smuzhiyun uint8 buffer[WLC_IOCTL_SMLEN] = {0, };
3569*4882a593Smuzhiyun wl_scanmac_t *sm = NULL;
3570*4882a593Smuzhiyun int len = 0;
3571*4882a593Smuzhiyun wl_scanmac_enable_t *sm_enable = NULL;
3572*4882a593Smuzhiyun #endif /* !LEGACY_RANDOM_MAC */
3573*4882a593Smuzhiyun
3574*4882a593Smuzhiyun #if defined(LEGACY_RANDOM_MAC)
3575*4882a593Smuzhiyun WL_ERR(("set original mac " MACDBG "\n",
3576*4882a593Smuzhiyun MAC2STRDBG((const u8 *)bcmcfg_to_prmry_ndev(cfg)->dev_addr)));
3577*4882a593Smuzhiyun
3578*4882a593Smuzhiyun err = wldev_iovar_setbuf_bsscfg(bcmcfg_to_prmry_ndev(cfg), "cur_etheraddr",
3579*4882a593Smuzhiyun bcmcfg_to_prmry_ndev(cfg)->dev_addr, ETH_ALEN,
3580*4882a593Smuzhiyun cfg->ioctl_buf, WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync);
3581*4882a593Smuzhiyun
3582*4882a593Smuzhiyun if (err != BCME_OK) {
3583*4882a593Smuzhiyun WL_ERR(("failed to set original MAC address\n"));
3584*4882a593Smuzhiyun } else {
3585*4882a593Smuzhiyun WL_ERR(("legacy random MAC disable done \n"));
3586*4882a593Smuzhiyun }
3587*4882a593Smuzhiyun #else
3588*4882a593Smuzhiyun sm = (wl_scanmac_t *)buffer;
3589*4882a593Smuzhiyun sm_enable = (wl_scanmac_enable_t *)sm->data;
3590*4882a593Smuzhiyun sm->len = sizeof(*sm_enable);
3591*4882a593Smuzhiyun /* Disable scanmac */
3592*4882a593Smuzhiyun sm_enable->enable = 0;
3593*4882a593Smuzhiyun len = OFFSETOF(wl_scanmac_t, data) + sm->len;
3594*4882a593Smuzhiyun
3595*4882a593Smuzhiyun sm->subcmd_id = WL_SCANMAC_SUBCMD_ENABLE;
3596*4882a593Smuzhiyun
3597*4882a593Smuzhiyun err = wldev_iovar_setbuf_bsscfg(dev, "scanmac",
3598*4882a593Smuzhiyun sm, len, cfg->ioctl_buf, WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync);
3599*4882a593Smuzhiyun
3600*4882a593Smuzhiyun if (err != BCME_OK) {
3601*4882a593Smuzhiyun WL_ERR(("failed to disable scanmac, err=%d\n", err));
3602*4882a593Smuzhiyun return err;
3603*4882a593Smuzhiyun }
3604*4882a593Smuzhiyun /* Clear scanmac enabled status */
3605*4882a593Smuzhiyun cfg->scanmac_enabled = 0;
3606*4882a593Smuzhiyun WL_DBG(("random MAC disable done\n"));
3607*4882a593Smuzhiyun #endif /* LEGACY_RANDOM_MAC */
3608*4882a593Smuzhiyun
3609*4882a593Smuzhiyun return err;
3610*4882a593Smuzhiyun }
3611*4882a593Smuzhiyun
wl_cfg80211_scan_mac_enable(struct net_device * dev)3612*4882a593Smuzhiyun int wl_cfg80211_scan_mac_enable(struct net_device *dev)
3613*4882a593Smuzhiyun {
3614*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
3615*4882a593Smuzhiyun s32 err = BCME_ERROR;
3616*4882a593Smuzhiyun uint8 buffer[WLC_IOCTL_SMLEN] = {0};
3617*4882a593Smuzhiyun wl_scanmac_t *sm = NULL;
3618*4882a593Smuzhiyun int len = 0;
3619*4882a593Smuzhiyun wl_scanmac_enable_t *sm_enable = NULL;
3620*4882a593Smuzhiyun
3621*4882a593Smuzhiyun /* Enable scan mac */
3622*4882a593Smuzhiyun sm = (wl_scanmac_t *)buffer;
3623*4882a593Smuzhiyun sm_enable = (wl_scanmac_enable_t *)sm->data;
3624*4882a593Smuzhiyun sm->len = sizeof(*sm_enable);
3625*4882a593Smuzhiyun sm_enable->enable = 1;
3626*4882a593Smuzhiyun len = OFFSETOF(wl_scanmac_t, data) + sm->len;
3627*4882a593Smuzhiyun sm->subcmd_id = WL_SCANMAC_SUBCMD_ENABLE;
3628*4882a593Smuzhiyun
3629*4882a593Smuzhiyun err = wldev_iovar_setbuf_bsscfg(dev, "scanmac",
3630*4882a593Smuzhiyun sm, len, cfg->ioctl_buf, WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync);
3631*4882a593Smuzhiyun if (unlikely(err)) {
3632*4882a593Smuzhiyun WL_ERR(("scanmac enable failed\n"));
3633*4882a593Smuzhiyun } else {
3634*4882a593Smuzhiyun /* Mark scanmac configured */
3635*4882a593Smuzhiyun cfg->scanmac_enabled = 1;
3636*4882a593Smuzhiyun }
3637*4882a593Smuzhiyun
3638*4882a593Smuzhiyun return err;
3639*4882a593Smuzhiyun }
3640*4882a593Smuzhiyun /*
3641*4882a593Smuzhiyun * This is new interface for mac randomization. It takes randmac and randmask
3642*4882a593Smuzhiyun * as arg and it uses scanmac iovar to offload the mac randomization to firmware.
3643*4882a593Smuzhiyun */
wl_cfg80211_scan_mac_config(struct net_device * dev,uint8 * rand_mac,uint8 * rand_mask)3644*4882a593Smuzhiyun int wl_cfg80211_scan_mac_config(struct net_device *dev, uint8 *rand_mac, uint8 *rand_mask)
3645*4882a593Smuzhiyun {
3646*4882a593Smuzhiyun int byte_index = 0;
3647*4882a593Smuzhiyun s32 err = BCME_ERROR;
3648*4882a593Smuzhiyun uint8 buffer[WLC_IOCTL_SMLEN] = {0};
3649*4882a593Smuzhiyun wl_scanmac_t *sm = NULL;
3650*4882a593Smuzhiyun int len = 0;
3651*4882a593Smuzhiyun wl_scanmac_config_t *sm_config = NULL;
3652*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
3653*4882a593Smuzhiyun uint8 random_mask_46_bits[ETHER_ADDR_LEN] = {0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
3654*4882a593Smuzhiyun
3655*4882a593Smuzhiyun if (rand_mac == NULL) {
3656*4882a593Smuzhiyun err = BCME_BADARG;
3657*4882a593Smuzhiyun WL_ERR(("fail to Set random mac, bad argument\n"));
3658*4882a593Smuzhiyun /* Disable the current scanmac config */
3659*4882a593Smuzhiyun return err;
3660*4882a593Smuzhiyun }
3661*4882a593Smuzhiyun
3662*4882a593Smuzhiyun if (ETHER_ISNULLADDR(rand_mac)) {
3663*4882a593Smuzhiyun WL_DBG(("fail to Set random mac, Invalid rand mac\n"));
3664*4882a593Smuzhiyun /* Disable the current scanmac config */
3665*4882a593Smuzhiyun return err;
3666*4882a593Smuzhiyun }
3667*4882a593Smuzhiyun
3668*4882a593Smuzhiyun /* Configure scanmac */
3669*4882a593Smuzhiyun (void)memset_s(buffer, sizeof(buffer), 0x0, sizeof(buffer));
3670*4882a593Smuzhiyun sm = (wl_scanmac_t *)buffer;
3671*4882a593Smuzhiyun sm_config = (wl_scanmac_config_t *)sm->data;
3672*4882a593Smuzhiyun sm->len = sizeof(*sm_config);
3673*4882a593Smuzhiyun sm->subcmd_id = WL_SCANMAC_SUBCMD_CONFIG;
3674*4882a593Smuzhiyun sm_config->scan_bitmap = WL_SCANMAC_SCAN_UNASSOC;
3675*4882a593Smuzhiyun #ifdef WL_USE_RANDOMIZED_SCAN
3676*4882a593Smuzhiyun sm_config->scan_bitmap |= WL_SCANMAC_SCAN_ASSOC_HOST;
3677*4882a593Smuzhiyun #endif /* WL_USE_RANDOMIZED_SCAN */
3678*4882a593Smuzhiyun /* Set randomize mac address recv from upper layer */
3679*4882a593Smuzhiyun (void)memcpy_s(&sm_config->mac.octet, ETH_ALEN, rand_mac, ETH_ALEN);
3680*4882a593Smuzhiyun
3681*4882a593Smuzhiyun /* Set randomize mask recv from upper layer */
3682*4882a593Smuzhiyun
3683*4882a593Smuzhiyun /* There is a difference in how to interpret rand_mask between
3684*4882a593Smuzhiyun * upperlayer and firmware. If the byte is set as FF then for
3685*4882a593Smuzhiyun * upper layer it means keep that byte and do not randomize whereas
3686*4882a593Smuzhiyun * for firmware it means randomize those bytes and vice versa. Hence
3687*4882a593Smuzhiyun * conversion is needed before setting the iovar
3688*4882a593Smuzhiyun */
3689*4882a593Smuzhiyun (void)memset_s(&sm_config->random_mask.octet, ETH_ALEN, 0x0, ETH_ALEN);
3690*4882a593Smuzhiyun /* Only byte randomization is supported currently. If mask recv is 0x0F
3691*4882a593Smuzhiyun * for a particular byte then it will be treated as no randomization
3692*4882a593Smuzhiyun * for that byte.
3693*4882a593Smuzhiyun */
3694*4882a593Smuzhiyun if (!rand_mask) {
3695*4882a593Smuzhiyun /* If rand_mask not provided, use 46_bits_mask */
3696*4882a593Smuzhiyun (void)memcpy_s(&sm_config->random_mask.octet, ETH_ALEN,
3697*4882a593Smuzhiyun random_mask_46_bits, ETH_ALEN);
3698*4882a593Smuzhiyun } else {
3699*4882a593Smuzhiyun while (byte_index < ETH_ALEN) {
3700*4882a593Smuzhiyun if (rand_mask[byte_index] == 0xFF) {
3701*4882a593Smuzhiyun sm_config->random_mask.octet[byte_index] = 0x00;
3702*4882a593Smuzhiyun } else if (rand_mask[byte_index] == 0x00) {
3703*4882a593Smuzhiyun sm_config->random_mask.octet[byte_index] = 0xFF;
3704*4882a593Smuzhiyun }
3705*4882a593Smuzhiyun byte_index++;
3706*4882a593Smuzhiyun }
3707*4882a593Smuzhiyun }
3708*4882a593Smuzhiyun
3709*4882a593Smuzhiyun WL_DBG(("recv random mac addr " MACDBG "recv rand mask" MACDBG "\n",
3710*4882a593Smuzhiyun MAC2STRDBG((const u8 *)&sm_config->mac.octet),
3711*4882a593Smuzhiyun MAC2STRDBG((const u8 *)&sm_config->random_mask)));
3712*4882a593Smuzhiyun
3713*4882a593Smuzhiyun len = OFFSETOF(wl_scanmac_t, data) + sm->len;
3714*4882a593Smuzhiyun err = wldev_iovar_setbuf_bsscfg(dev, "scanmac",
3715*4882a593Smuzhiyun sm, len, cfg->ioctl_buf, WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync);
3716*4882a593Smuzhiyun
3717*4882a593Smuzhiyun if (err != BCME_OK) {
3718*4882a593Smuzhiyun WL_ERR(("failed scanmac configuration\n"));
3719*4882a593Smuzhiyun
3720*4882a593Smuzhiyun /* Disable scan mac for clean-up */
3721*4882a593Smuzhiyun return err;
3722*4882a593Smuzhiyun }
3723*4882a593Smuzhiyun WL_INFORM_MEM(("scanmac configured"));
3724*4882a593Smuzhiyun cfg->scanmac_config = true;
3725*4882a593Smuzhiyun
3726*4882a593Smuzhiyun return err;
3727*4882a593Smuzhiyun }
3728*4882a593Smuzhiyun
3729*4882a593Smuzhiyun int
wl_cfg80211_scan_mac_disable(struct net_device * dev)3730*4882a593Smuzhiyun wl_cfg80211_scan_mac_disable(struct net_device *dev)
3731*4882a593Smuzhiyun {
3732*4882a593Smuzhiyun s32 err = BCME_ERROR;
3733*4882a593Smuzhiyun
3734*4882a593Smuzhiyun err = wl_cfg80211_random_mac_disable(dev);
3735*4882a593Smuzhiyun
3736*4882a593Smuzhiyun return err;
3737*4882a593Smuzhiyun }
3738*4882a593Smuzhiyun #endif /* SUPPORT_RANDOM_MAC_SCAN */
3739*4882a593Smuzhiyun
3740*4882a593Smuzhiyun #ifdef WL_SCHED_SCAN
3741*4882a593Smuzhiyun #define PNO_TIME 30
3742*4882a593Smuzhiyun #define PNO_REPEAT 4
3743*4882a593Smuzhiyun #define PNO_FREQ_EXPO_MAX 2
3744*4882a593Smuzhiyun #define PNO_ADAPTIVE_SCAN_LIMIT 60
3745*4882a593Smuzhiyun static bool
is_ssid_in_list(struct cfg80211_ssid * ssid,struct cfg80211_ssid * ssid_list,int count)3746*4882a593Smuzhiyun is_ssid_in_list(struct cfg80211_ssid *ssid, struct cfg80211_ssid *ssid_list, int count)
3747*4882a593Smuzhiyun {
3748*4882a593Smuzhiyun int i;
3749*4882a593Smuzhiyun
3750*4882a593Smuzhiyun if (!ssid || !ssid_list)
3751*4882a593Smuzhiyun return FALSE;
3752*4882a593Smuzhiyun
3753*4882a593Smuzhiyun for (i = 0; i < count; i++) {
3754*4882a593Smuzhiyun if (ssid->ssid_len == ssid_list[i].ssid_len) {
3755*4882a593Smuzhiyun if (strncmp(ssid->ssid, ssid_list[i].ssid, ssid->ssid_len) == 0)
3756*4882a593Smuzhiyun return TRUE;
3757*4882a593Smuzhiyun }
3758*4882a593Smuzhiyun }
3759*4882a593Smuzhiyun return FALSE;
3760*4882a593Smuzhiyun }
3761*4882a593Smuzhiyun
3762*4882a593Smuzhiyun int
wl_cfg80211_sched_scan_start(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_sched_scan_request * request)3763*4882a593Smuzhiyun wl_cfg80211_sched_scan_start(struct wiphy *wiphy,
3764*4882a593Smuzhiyun struct net_device *dev,
3765*4882a593Smuzhiyun struct cfg80211_sched_scan_request *request)
3766*4882a593Smuzhiyun {
3767*4882a593Smuzhiyun u16 chan_list[WL_NUMCHANNELS] = {0};
3768*4882a593Smuzhiyun u32 num_channels = 0;
3769*4882a593Smuzhiyun ushort pno_time;
3770*4882a593Smuzhiyun int pno_repeat = PNO_REPEAT;
3771*4882a593Smuzhiyun int pno_freq_expo_max = PNO_FREQ_EXPO_MAX;
3772*4882a593Smuzhiyun wlc_ssid_ext_t ssids_local[MAX_PFN_LIST_COUNT];
3773*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
3774*4882a593Smuzhiyun dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
3775*4882a593Smuzhiyun struct cfg80211_ssid *ssid = NULL;
3776*4882a593Smuzhiyun struct cfg80211_ssid *hidden_ssid_list = NULL;
3777*4882a593Smuzhiyun log_conn_event_t *event_data = NULL;
3778*4882a593Smuzhiyun tlv_log *tlv_data = NULL;
3779*4882a593Smuzhiyun u32 alloc_len = 0;
3780*4882a593Smuzhiyun u32 payload_len;
3781*4882a593Smuzhiyun int ssid_cnt = 0;
3782*4882a593Smuzhiyun int i;
3783*4882a593Smuzhiyun int ret = 0;
3784*4882a593Smuzhiyun unsigned long flags;
3785*4882a593Smuzhiyun
3786*4882a593Smuzhiyun if (!request) {
3787*4882a593Smuzhiyun WL_ERR(("Sched scan request was NULL\n"));
3788*4882a593Smuzhiyun return -EINVAL;
3789*4882a593Smuzhiyun }
3790*4882a593Smuzhiyun
3791*4882a593Smuzhiyun if ((request->n_scan_plans == 1) && request->scan_plans &&
3792*4882a593Smuzhiyun (request->scan_plans->interval > PNO_ADAPTIVE_SCAN_LIMIT)) {
3793*4882a593Smuzhiyun /* If the host gives a high value for scan interval, then
3794*4882a593Smuzhiyun * doing adaptive scan doesn't make sense. Better stick to the
3795*4882a593Smuzhiyun * scan interval that host gives.
3796*4882a593Smuzhiyun */
3797*4882a593Smuzhiyun pno_time = request->scan_plans->interval;
3798*4882a593Smuzhiyun pno_repeat = 0;
3799*4882a593Smuzhiyun pno_freq_expo_max = 0;
3800*4882a593Smuzhiyun } else {
3801*4882a593Smuzhiyun /* Run adaptive PNO */
3802*4882a593Smuzhiyun pno_time = PNO_TIME;
3803*4882a593Smuzhiyun }
3804*4882a593Smuzhiyun
3805*4882a593Smuzhiyun WL_DBG(("Enter. ssids:%d match_sets:%d pno_time:%d pno_repeat:%d channels:%d\n",
3806*4882a593Smuzhiyun request->n_ssids, request->n_match_sets,
3807*4882a593Smuzhiyun pno_time, pno_repeat, request->n_channels));
3808*4882a593Smuzhiyun
3809*4882a593Smuzhiyun if (!request->n_ssids || !request->n_match_sets) {
3810*4882a593Smuzhiyun WL_ERR(("Invalid sched scan req!! n_ssids:%d \n", request->n_ssids));
3811*4882a593Smuzhiyun return -EINVAL;
3812*4882a593Smuzhiyun }
3813*4882a593Smuzhiyun
3814*4882a593Smuzhiyun bzero(&ssids_local, sizeof(ssids_local));
3815*4882a593Smuzhiyun
3816*4882a593Smuzhiyun if (request->n_ssids > 0) {
3817*4882a593Smuzhiyun hidden_ssid_list = request->ssids;
3818*4882a593Smuzhiyun }
3819*4882a593Smuzhiyun
3820*4882a593Smuzhiyun if (request->n_channels && request->n_channels < WL_NUMCHANNELS) {
3821*4882a593Smuzhiyun /* get channel list. Note PNO uses channels and not chanspecs */
3822*4882a593Smuzhiyun wl_cfgscan_populate_scan_channels(cfg,
3823*4882a593Smuzhiyun request->channels, request->n_channels,
3824*4882a593Smuzhiyun chan_list, &num_channels, false, false);
3825*4882a593Smuzhiyun }
3826*4882a593Smuzhiyun
3827*4882a593Smuzhiyun if (DBG_RING_ACTIVE(dhdp, DHD_EVENT_RING_ID)) {
3828*4882a593Smuzhiyun alloc_len = sizeof(log_conn_event_t) + sizeof(tlv_log) + DOT11_MAX_SSID_LEN;
3829*4882a593Smuzhiyun event_data = (log_conn_event_t *)MALLOCZ(cfg->osh, alloc_len);
3830*4882a593Smuzhiyun if (!event_data) {
3831*4882a593Smuzhiyun WL_ERR(("%s: failed to allocate log_conn_event_t with "
3832*4882a593Smuzhiyun "length(%d)\n", __func__, alloc_len));
3833*4882a593Smuzhiyun return -ENOMEM;
3834*4882a593Smuzhiyun }
3835*4882a593Smuzhiyun }
3836*4882a593Smuzhiyun for (i = 0; i < request->n_match_sets && ssid_cnt < MAX_PFN_LIST_COUNT; i++) {
3837*4882a593Smuzhiyun ssid = &request->match_sets[i].ssid;
3838*4882a593Smuzhiyun /* No need to include null ssid */
3839*4882a593Smuzhiyun if (ssid->ssid_len) {
3840*4882a593Smuzhiyun ssids_local[ssid_cnt].SSID_len = MIN(ssid->ssid_len,
3841*4882a593Smuzhiyun (uint32)DOT11_MAX_SSID_LEN);
3842*4882a593Smuzhiyun /* In previous step max SSID_len is limited to DOT11_MAX_SSID_LEN,
3843*4882a593Smuzhiyun * returning void
3844*4882a593Smuzhiyun */
3845*4882a593Smuzhiyun (void)memcpy_s(ssids_local[ssid_cnt].SSID, DOT11_MAX_SSID_LEN, ssid->ssid,
3846*4882a593Smuzhiyun ssids_local[ssid_cnt].SSID_len);
3847*4882a593Smuzhiyun if (is_ssid_in_list(ssid, hidden_ssid_list, request->n_ssids)) {
3848*4882a593Smuzhiyun ssids_local[ssid_cnt].hidden = TRUE;
3849*4882a593Smuzhiyun WL_PNO((">>> PNO hidden SSID (%s) \n", ssid->ssid));
3850*4882a593Smuzhiyun } else {
3851*4882a593Smuzhiyun ssids_local[ssid_cnt].hidden = FALSE;
3852*4882a593Smuzhiyun WL_PNO((">>> PNO non-hidden SSID (%s) \n", ssid->ssid));
3853*4882a593Smuzhiyun }
3854*4882a593Smuzhiyun #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 15, 0))
3855*4882a593Smuzhiyun if (request->match_sets[i].rssi_thold != NL80211_SCAN_RSSI_THOLD_OFF) {
3856*4882a593Smuzhiyun ssids_local[ssid_cnt].rssi_thresh =
3857*4882a593Smuzhiyun (int8)request->match_sets[i].rssi_thold;
3858*4882a593Smuzhiyun }
3859*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(3, 15, 0)) */
3860*4882a593Smuzhiyun ssid_cnt++;
3861*4882a593Smuzhiyun }
3862*4882a593Smuzhiyun }
3863*4882a593Smuzhiyun
3864*4882a593Smuzhiyun if (ssid_cnt) {
3865*4882a593Smuzhiyun #if defined(BCMDONGLEHOST)
3866*4882a593Smuzhiyun if ((ret = dhd_dev_pno_set_for_ssid(dev, ssids_local, ssid_cnt,
3867*4882a593Smuzhiyun pno_time, pno_repeat, pno_freq_expo_max,
3868*4882a593Smuzhiyun (num_channels ? chan_list : NULL), num_channels)) < 0) {
3869*4882a593Smuzhiyun WL_ERR(("PNO setup failed!! ret=%d \n", ret));
3870*4882a593Smuzhiyun ret = -EINVAL;
3871*4882a593Smuzhiyun goto exit;
3872*4882a593Smuzhiyun }
3873*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
3874*4882a593Smuzhiyun
3875*4882a593Smuzhiyun if (DBG_RING_ACTIVE(dhdp, DHD_EVENT_RING_ID)) {
3876*4882a593Smuzhiyun /*
3877*4882a593Smuzhiyun * purposefully logging here to make sure that
3878*4882a593Smuzhiyun * firmware configuration was successful
3879*4882a593Smuzhiyun */
3880*4882a593Smuzhiyun for (i = 0; i < ssid_cnt; i++) {
3881*4882a593Smuzhiyun payload_len = sizeof(log_conn_event_t);
3882*4882a593Smuzhiyun event_data->event = WIFI_EVENT_DRIVER_PNO_ADD;
3883*4882a593Smuzhiyun tlv_data = event_data->tlvs;
3884*4882a593Smuzhiyun /* ssid */
3885*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_SSID;
3886*4882a593Smuzhiyun tlv_data->len = ssids_local[i].SSID_len;
3887*4882a593Smuzhiyun (void)memcpy_s(tlv_data->value, DOT11_MAX_SSID_LEN,
3888*4882a593Smuzhiyun ssids_local[i].SSID, ssids_local[i].SSID_len);
3889*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
3890*4882a593Smuzhiyun
3891*4882a593Smuzhiyun dhd_os_push_push_ring_data(dhdp, DHD_EVENT_RING_ID,
3892*4882a593Smuzhiyun event_data, payload_len);
3893*4882a593Smuzhiyun }
3894*4882a593Smuzhiyun }
3895*4882a593Smuzhiyun
3896*4882a593Smuzhiyun WL_CFG_DRV_LOCK(&cfg->cfgdrv_lock, flags);
3897*4882a593Smuzhiyun cfg->sched_scan_req = request;
3898*4882a593Smuzhiyun WL_CFG_DRV_UNLOCK(&cfg->cfgdrv_lock, flags);
3899*4882a593Smuzhiyun } else {
3900*4882a593Smuzhiyun ret = -EINVAL;
3901*4882a593Smuzhiyun }
3902*4882a593Smuzhiyun
3903*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) && defined(SUPPORT_RANDOM_MAC_SCAN)
3904*4882a593Smuzhiyun if ((ret = wl_config_scan_macaddr(cfg, dev,
3905*4882a593Smuzhiyun (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR),
3906*4882a593Smuzhiyun request->mac_addr, request->mac_addr_mask)) != BCME_OK) {
3907*4882a593Smuzhiyun WL_ERR(("scanmac addr config failed\n"));
3908*4882a593Smuzhiyun /* Cleanup the states and stop the pno */
3909*4882a593Smuzhiyun if (dhd_dev_pno_stop_for_ssid(dev) < 0) {
3910*4882a593Smuzhiyun WL_ERR(("PNO Stop for SSID failed"));
3911*4882a593Smuzhiyun }
3912*4882a593Smuzhiyun WL_CFG_DRV_LOCK(&cfg->cfgdrv_lock, flags);
3913*4882a593Smuzhiyun cfg->sched_scan_req = NULL;
3914*4882a593Smuzhiyun cfg->sched_scan_running = FALSE;
3915*4882a593Smuzhiyun WL_CFG_DRV_UNLOCK(&cfg->cfgdrv_lock, flags);
3916*4882a593Smuzhiyun goto exit;
3917*4882a593Smuzhiyun }
3918*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) && (defined(SUPPORT_RANDOM_MAC_SCAN)) */
3919*4882a593Smuzhiyun
3920*4882a593Smuzhiyun exit:
3921*4882a593Smuzhiyun if (event_data) {
3922*4882a593Smuzhiyun MFREE(cfg->osh, event_data, alloc_len);
3923*4882a593Smuzhiyun }
3924*4882a593Smuzhiyun return ret;
3925*4882a593Smuzhiyun }
3926*4882a593Smuzhiyun
3927*4882a593Smuzhiyun int
3928*4882a593Smuzhiyun #if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 11, 0))
wl_cfg80211_sched_scan_stop(struct wiphy * wiphy,struct net_device * dev,u64 reqid)3929*4882a593Smuzhiyun wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, u64 reqid)
3930*4882a593Smuzhiyun #else
3931*4882a593Smuzhiyun wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev)
3932*4882a593Smuzhiyun #endif /* LINUX_VER > 4.11 */
3933*4882a593Smuzhiyun {
3934*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
3935*4882a593Smuzhiyun dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
3936*4882a593Smuzhiyun
3937*4882a593Smuzhiyun WL_DBG(("Enter \n"));
3938*4882a593Smuzhiyun WL_PNO((">>> SCHED SCAN STOP\n"));
3939*4882a593Smuzhiyun
3940*4882a593Smuzhiyun #if defined(BCMDONGLEHOST)
3941*4882a593Smuzhiyun if (dhd_dev_pno_stop_for_ssid(dev) < 0) {
3942*4882a593Smuzhiyun WL_ERR(("PNO Stop for SSID failed"));
3943*4882a593Smuzhiyun } else {
3944*4882a593Smuzhiyun /*
3945*4882a593Smuzhiyun * purposefully logging here to make sure that
3946*4882a593Smuzhiyun * firmware configuration was successful
3947*4882a593Smuzhiyun */
3948*4882a593Smuzhiyun DBG_EVENT_LOG(dhdp, WIFI_EVENT_DRIVER_PNO_REMOVE);
3949*4882a593Smuzhiyun }
3950*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
3951*4882a593Smuzhiyun
3952*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
3953*4882a593Smuzhiyun if (cfg->sched_scan_req) {
3954*4882a593Smuzhiyun WL_PNO((">>> Sched scan running. Aborting it..\n"));
3955*4882a593Smuzhiyun _wl_cfgscan_cancel_scan(cfg);
3956*4882a593Smuzhiyun }
3957*4882a593Smuzhiyun cfg->sched_scan_req = NULL;
3958*4882a593Smuzhiyun cfg->sched_scan_running = FALSE;
3959*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
3960*4882a593Smuzhiyun
3961*4882a593Smuzhiyun return 0;
3962*4882a593Smuzhiyun }
3963*4882a593Smuzhiyun #endif /* WL_SCHED_SCAN */
3964*4882a593Smuzhiyun
3965*4882a593Smuzhiyun #ifdef WES_SUPPORT
3966*4882a593Smuzhiyun #ifdef CUSTOMER_SCAN_TIMEOUT_SETTING
wl_cfg80211_custom_scan_time(struct net_device * dev,enum wl_custom_scan_time_type type,int time)3967*4882a593Smuzhiyun s32 wl_cfg80211_custom_scan_time(struct net_device *dev,
3968*4882a593Smuzhiyun enum wl_custom_scan_time_type type, int time)
3969*4882a593Smuzhiyun {
3970*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
3971*4882a593Smuzhiyun
3972*4882a593Smuzhiyun if (cfg == NULL) {
3973*4882a593Smuzhiyun return FALSE;
3974*4882a593Smuzhiyun }
3975*4882a593Smuzhiyun
3976*4882a593Smuzhiyun switch (type) {
3977*4882a593Smuzhiyun case WL_CUSTOM_SCAN_CHANNEL_TIME :
3978*4882a593Smuzhiyun WL_ERR(("Scan Channel Time %d\n", time));
3979*4882a593Smuzhiyun cfg->custom_scan_channel_time = time;
3980*4882a593Smuzhiyun break;
3981*4882a593Smuzhiyun case WL_CUSTOM_SCAN_UNASSOC_TIME :
3982*4882a593Smuzhiyun WL_ERR(("Scan Unassoc Time %d\n", time));
3983*4882a593Smuzhiyun cfg->custom_scan_unassoc_time = time;
3984*4882a593Smuzhiyun break;
3985*4882a593Smuzhiyun case WL_CUSTOM_SCAN_PASSIVE_TIME :
3986*4882a593Smuzhiyun WL_ERR(("Scan Passive Time %d\n", time));
3987*4882a593Smuzhiyun cfg->custom_scan_passive_time = time;
3988*4882a593Smuzhiyun break;
3989*4882a593Smuzhiyun case WL_CUSTOM_SCAN_HOME_TIME :
3990*4882a593Smuzhiyun WL_ERR(("Scan Home Time %d\n", time));
3991*4882a593Smuzhiyun cfg->custom_scan_home_time = time;
3992*4882a593Smuzhiyun break;
3993*4882a593Smuzhiyun case WL_CUSTOM_SCAN_HOME_AWAY_TIME :
3994*4882a593Smuzhiyun WL_ERR(("Scan Home Away Time %d\n", time));
3995*4882a593Smuzhiyun cfg->custom_scan_home_away_time = time;
3996*4882a593Smuzhiyun break;
3997*4882a593Smuzhiyun default:
3998*4882a593Smuzhiyun return FALSE;
3999*4882a593Smuzhiyun }
4000*4882a593Smuzhiyun return TRUE;
4001*4882a593Smuzhiyun }
4002*4882a593Smuzhiyun #endif /* CUSTOMER_SCAN_TIMEOUT_SETTING */
4003*4882a593Smuzhiyun #endif /* WES_SUPPORT */
4004*4882a593Smuzhiyun
4005*4882a593Smuzhiyun #ifdef CUSTOMER_HW4_DEBUG
4006*4882a593Smuzhiyun uint prev_dhd_console_ms = 0;
4007*4882a593Smuzhiyun u32 prev_wl_dbg_level = 0;
4008*4882a593Smuzhiyun static void wl_scan_timeout_dbg_set(void);
4009*4882a593Smuzhiyun
wl_scan_timeout_dbg_set(void)4010*4882a593Smuzhiyun static void wl_scan_timeout_dbg_set(void)
4011*4882a593Smuzhiyun {
4012*4882a593Smuzhiyun WL_ERR(("Enter \n"));
4013*4882a593Smuzhiyun prev_dhd_console_ms = dhd_console_ms;
4014*4882a593Smuzhiyun prev_wl_dbg_level = wl_dbg_level;
4015*4882a593Smuzhiyun
4016*4882a593Smuzhiyun dhd_console_ms = 1;
4017*4882a593Smuzhiyun wl_dbg_level |= (WL_DBG_ERR | WL_DBG_P2P_ACTION | WL_DBG_SCAN);
4018*4882a593Smuzhiyun
4019*4882a593Smuzhiyun wl_scan_timeout_dbg_enabled = 1;
4020*4882a593Smuzhiyun }
wl_scan_timeout_dbg_clear(void)4021*4882a593Smuzhiyun void wl_scan_timeout_dbg_clear(void)
4022*4882a593Smuzhiyun {
4023*4882a593Smuzhiyun WL_ERR(("Enter \n"));
4024*4882a593Smuzhiyun dhd_console_ms = prev_dhd_console_ms;
4025*4882a593Smuzhiyun wl_dbg_level = prev_wl_dbg_level;
4026*4882a593Smuzhiyun
4027*4882a593Smuzhiyun wl_scan_timeout_dbg_enabled = 0;
4028*4882a593Smuzhiyun }
4029*4882a593Smuzhiyun #endif /* CUSTOMER_HW4_DEBUG */
4030*4882a593Smuzhiyun
wl_scan_timeout(unsigned long data)4031*4882a593Smuzhiyun static void wl_scan_timeout(unsigned long data)
4032*4882a593Smuzhiyun {
4033*4882a593Smuzhiyun wl_event_msg_t msg;
4034*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)data;
4035*4882a593Smuzhiyun struct wireless_dev *wdev = NULL;
4036*4882a593Smuzhiyun struct net_device *ndev = NULL;
4037*4882a593Smuzhiyun #if 0
4038*4882a593Smuzhiyun wl_scan_results_t *bss_list;
4039*4882a593Smuzhiyun wl_bss_info_t *bi = NULL;
4040*4882a593Smuzhiyun s32 i;
4041*4882a593Smuzhiyun u32 channel;
4042*4882a593Smuzhiyun #endif
4043*4882a593Smuzhiyun u64 cur_time = OSL_LOCALTIME_NS();
4044*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
4045*4882a593Smuzhiyun dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
4046*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
4047*4882a593Smuzhiyun unsigned long flags;
4048*4882a593Smuzhiyun #ifdef RTT_SUPPORT
4049*4882a593Smuzhiyun rtt_status_info_t *rtt_status = NULL;
4050*4882a593Smuzhiyun UNUSED_PARAMETER(rtt_status);
4051*4882a593Smuzhiyun #endif /* RTT_SUPPORT */
4052*4882a593Smuzhiyun
4053*4882a593Smuzhiyun UNUSED_PARAMETER(cur_time);
4054*4882a593Smuzhiyun WL_CFG_DRV_LOCK(&cfg->cfgdrv_lock, flags);
4055*4882a593Smuzhiyun if (!(cfg->scan_request)) {
4056*4882a593Smuzhiyun WL_ERR(("timer expired but no scan request\n"));
4057*4882a593Smuzhiyun WL_CFG_DRV_UNLOCK(&cfg->cfgdrv_lock, flags);
4058*4882a593Smuzhiyun return;
4059*4882a593Smuzhiyun }
4060*4882a593Smuzhiyun
4061*4882a593Smuzhiyun wdev = GET_SCAN_WDEV(cfg->scan_request);
4062*4882a593Smuzhiyun WL_CFG_DRV_UNLOCK(&cfg->cfgdrv_lock, flags);
4063*4882a593Smuzhiyun
4064*4882a593Smuzhiyun if (!wdev) {
4065*4882a593Smuzhiyun WL_ERR(("No wireless_dev present\n"));
4066*4882a593Smuzhiyun return;
4067*4882a593Smuzhiyun }
4068*4882a593Smuzhiyun
4069*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
4070*4882a593Smuzhiyun if (dhd_query_bus_erros(dhdp)) {
4071*4882a593Smuzhiyun return;
4072*4882a593Smuzhiyun }
4073*4882a593Smuzhiyun #if defined(DHD_KERNEL_SCHED_DEBUG) && defined(DHD_FW_COREDUMP)
4074*4882a593Smuzhiyun /* DHD triggers Kernel panic if the SCAN timeout occurrs
4075*4882a593Smuzhiyun * due to tasklet or workqueue scheduling problems in the Linux Kernel.
4076*4882a593Smuzhiyun * Customer informs that it is hard to find any clue from the
4077*4882a593Smuzhiyun * host memory dump since the important tasklet or workqueue information
4078*4882a593Smuzhiyun * is already disappered due the latency while printing out the timestamp
4079*4882a593Smuzhiyun * logs for debugging scan timeout issue.
4080*4882a593Smuzhiyun * For this reason, customer requestes us to trigger Kernel Panic rather than
4081*4882a593Smuzhiyun * taking a SOCRAM dump.
4082*4882a593Smuzhiyun */
4083*4882a593Smuzhiyun if (dhdp->memdump_enabled == DUMP_MEMFILE_BUGON &&
4084*4882a593Smuzhiyun ((cfg->tsinfo.scan_deq < cfg->tsinfo.scan_enq) ||
4085*4882a593Smuzhiyun dhd_bus_query_dpc_sched_errors(dhdp))) {
4086*4882a593Smuzhiyun WL_ERR(("****SCAN event timeout due to scheduling problem\n"));
4087*4882a593Smuzhiyun /* change g_assert_type to trigger Kernel panic */
4088*4882a593Smuzhiyun g_assert_type = 2;
4089*4882a593Smuzhiyun #ifdef RTT_SUPPORT
4090*4882a593Smuzhiyun rtt_status = GET_RTTSTATE(dhdp);
4091*4882a593Smuzhiyun #endif /* RTT_SUPPORT */
4092*4882a593Smuzhiyun WL_ERR(("***SCAN event timeout. WQ state:0x%x scan_enq_time:"SEC_USEC_FMT
4093*4882a593Smuzhiyun " evt_hdlr_entry_time:"SEC_USEC_FMT" evt_deq_time:"SEC_USEC_FMT
4094*4882a593Smuzhiyun "\nscan_deq_time:"SEC_USEC_FMT" scan_hdlr_cmplt_time:"SEC_USEC_FMT
4095*4882a593Smuzhiyun " scan_cmplt_time:"SEC_USEC_FMT" evt_hdlr_exit_time:"SEC_USEC_FMT
4096*4882a593Smuzhiyun "\ncurrent_time:"SEC_USEC_FMT"\n", work_busy(&cfg->event_work),
4097*4882a593Smuzhiyun GET_SEC_USEC(cfg->tsinfo.scan_enq),
4098*4882a593Smuzhiyun GET_SEC_USEC(cfg->tsinfo.wl_evt_hdlr_entry),
4099*4882a593Smuzhiyun GET_SEC_USEC(cfg->tsinfo.wl_evt_deq),
4100*4882a593Smuzhiyun GET_SEC_USEC(cfg->tsinfo.scan_deq),
4101*4882a593Smuzhiyun GET_SEC_USEC(cfg->tsinfo.scan_hdlr_cmplt),
4102*4882a593Smuzhiyun GET_SEC_USEC(cfg->tsinfo.scan_cmplt),
4103*4882a593Smuzhiyun GET_SEC_USEC(cfg->tsinfo.wl_evt_hdlr_exit), GET_SEC_USEC(cur_time)));
4104*4882a593Smuzhiyun if (cfg->tsinfo.scan_enq) {
4105*4882a593Smuzhiyun WL_ERR(("Elapsed time(ns): %llu\n", (cur_time - cfg->tsinfo.scan_enq)));
4106*4882a593Smuzhiyun }
4107*4882a593Smuzhiyun WL_ERR(("lock_states:[%d:%d:%d:%d:%d:%d]\n",
4108*4882a593Smuzhiyun mutex_is_locked(&cfg->if_sync),
4109*4882a593Smuzhiyun mutex_is_locked(&cfg->usr_sync),
4110*4882a593Smuzhiyun mutex_is_locked(&cfg->pm_sync),
4111*4882a593Smuzhiyun mutex_is_locked(&cfg->scan_sync),
4112*4882a593Smuzhiyun spin_is_locked(&cfg->cfgdrv_lock),
4113*4882a593Smuzhiyun spin_is_locked(&cfg->eq_lock)));
4114*4882a593Smuzhiyun #ifdef RTT_SUPPORT
4115*4882a593Smuzhiyun WL_ERR(("RTT lock_state:[%d]\n",
4116*4882a593Smuzhiyun mutex_is_locked(&rtt_status->rtt_mutex)));
4117*4882a593Smuzhiyun #ifdef WL_NAN
4118*4882a593Smuzhiyun WL_ERR(("RTT and Geofence lock_states:[%d:%d]\n",
4119*4882a593Smuzhiyun mutex_is_locked(&cfg->nancfg->nan_sync),
4120*4882a593Smuzhiyun mutex_is_locked(&(rtt_status)->geofence_mutex)));
4121*4882a593Smuzhiyun #endif /* WL_NAN */
4122*4882a593Smuzhiyun #endif /* RTT_SUPPORT */
4123*4882a593Smuzhiyun
4124*4882a593Smuzhiyun /* use ASSERT() to trigger panic */
4125*4882a593Smuzhiyun ASSERT(0);
4126*4882a593Smuzhiyun }
4127*4882a593Smuzhiyun #endif /* DHD_KERNEL_SCHED_DEBUG && DHD_FW_COREDUMP */
4128*4882a593Smuzhiyun dhd_bus_intr_count_dump(dhdp);
4129*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
4130*4882a593Smuzhiyun
4131*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) && !defined(CONFIG_MODULES)
4132*4882a593Smuzhiyun /* Print WQ states. Enable only for in-built drivers as the symbol is not exported */
4133*4882a593Smuzhiyun show_workqueue_state();
4134*4882a593Smuzhiyun #endif /* LINUX_VER >= 4.1 && !CONFIG_MODULES */
4135*4882a593Smuzhiyun
4136*4882a593Smuzhiyun #if 0
4137*4882a593Smuzhiyun bss_list = wl_escan_get_buf(cfg, FALSE);
4138*4882a593Smuzhiyun if (!bss_list) {
4139*4882a593Smuzhiyun WL_ERR(("bss_list is null. Didn't receive any partial scan results\n"));
4140*4882a593Smuzhiyun } else {
4141*4882a593Smuzhiyun WL_ERR(("Dump scan buffer:\n"
4142*4882a593Smuzhiyun "scanned AP count (%d)\n", bss_list->count));
4143*4882a593Smuzhiyun
4144*4882a593Smuzhiyun bi = next_bss(bss_list, bi);
4145*4882a593Smuzhiyun for_each_bss(bss_list, bi, i) {
4146*4882a593Smuzhiyun channel = wf_chspec_ctlchan(wl_chspec_driver_to_host(bi->chanspec));
4147*4882a593Smuzhiyun WL_ERR(("SSID :%s Channel :%d\n", bi->SSID, channel));
4148*4882a593Smuzhiyun }
4149*4882a593Smuzhiyun }
4150*4882a593Smuzhiyun #endif
4151*4882a593Smuzhiyun
4152*4882a593Smuzhiyun ndev = wdev_to_wlc_ndev(wdev, cfg);
4153*4882a593Smuzhiyun bzero(&msg, sizeof(wl_event_msg_t));
4154*4882a593Smuzhiyun WL_ERR(("timer expired\n"));
4155*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
4156*4882a593Smuzhiyun dhdp->scan_timeout_occurred = TRUE;
4157*4882a593Smuzhiyun #ifdef BCMPCIE
4158*4882a593Smuzhiyun if (!dhd_pcie_dump_int_regs(dhdp)) {
4159*4882a593Smuzhiyun WL_ERR(("%s : PCIe link might be down\n", __FUNCTION__));
4160*4882a593Smuzhiyun dhd_bus_set_linkdown(dhdp, TRUE);
4161*4882a593Smuzhiyun dhdp->hang_reason = HANG_REASON_PCIE_LINK_DOWN_EP_DETECT;
4162*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(OEM_ANDROID)
4163*4882a593Smuzhiyun dhd_os_send_hang_message(dhdp);
4164*4882a593Smuzhiyun #else
4165*4882a593Smuzhiyun WL_ERR(("%s: HANG event is unsupported\n", __FUNCTION__));
4166*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) && OEM_ANDROID */
4167*4882a593Smuzhiyun }
4168*4882a593Smuzhiyun
4169*4882a593Smuzhiyun dhd_pcie_dump_rc_conf_space_cap(dhdp);
4170*4882a593Smuzhiyun #endif /* BCMPCIE */
4171*4882a593Smuzhiyun #if 0
4172*4882a593Smuzhiyun if (!dhd_bus_get_linkdown(dhdp) && dhdp->memdump_enabled) {
4173*4882a593Smuzhiyun dhdp->memdump_type = DUMP_TYPE_SCAN_TIMEOUT;
4174*4882a593Smuzhiyun dhd_bus_mem_dump(dhdp);
4175*4882a593Smuzhiyun }
4176*4882a593Smuzhiyun #endif /* DHD_FW_COREDUMP */
4177*4882a593Smuzhiyun /*
4178*4882a593Smuzhiyun * For the memdump sanity, blocking bus transactions for a while
4179*4882a593Smuzhiyun * Keeping it TRUE causes the sequential private cmd error
4180*4882a593Smuzhiyun */
4181*4882a593Smuzhiyun dhdp->scan_timeout_occurred = FALSE;
4182*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
4183*4882a593Smuzhiyun msg.event_type = hton32(WLC_E_ESCAN_RESULT);
4184*4882a593Smuzhiyun msg.status = hton32(WLC_E_STATUS_TIMEOUT);
4185*4882a593Smuzhiyun msg.reason = 0xFFFFFFFF;
4186*4882a593Smuzhiyun wl_cfg80211_event(ndev, &msg, NULL);
4187*4882a593Smuzhiyun #ifdef CUSTOMER_HW4_DEBUG
4188*4882a593Smuzhiyun if (!wl_scan_timeout_dbg_enabled)
4189*4882a593Smuzhiyun wl_scan_timeout_dbg_set();
4190*4882a593Smuzhiyun #endif /* CUSTOMER_HW4_DEBUG */
4191*4882a593Smuzhiyun
4192*4882a593Smuzhiyun #if defined(BCMDONGLEHOST) && defined(OEM_ANDROID)
4193*4882a593Smuzhiyun DHD_ENABLE_RUNTIME_PM(dhdp);
4194*4882a593Smuzhiyun #endif /* BCMDONGLEHOST && OEM_ANDROID */
4195*4882a593Smuzhiyun
4196*4882a593Smuzhiyun }
4197*4882a593Smuzhiyun
wl_init_scan(struct bcm_cfg80211 * cfg)4198*4882a593Smuzhiyun s32 wl_init_scan(struct bcm_cfg80211 *cfg)
4199*4882a593Smuzhiyun {
4200*4882a593Smuzhiyun int err = 0;
4201*4882a593Smuzhiyun
4202*4882a593Smuzhiyun cfg->evt_handler[WLC_E_ESCAN_RESULT] = wl_escan_handler;
4203*4882a593Smuzhiyun cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
4204*4882a593Smuzhiyun wl_escan_init_sync_id(cfg);
4205*4882a593Smuzhiyun
4206*4882a593Smuzhiyun /* Init scan_timeout timer */
4207*4882a593Smuzhiyun init_timer_compat(&cfg->scan_timeout, wl_scan_timeout, cfg);
4208*4882a593Smuzhiyun
4209*4882a593Smuzhiyun wl_cfg80211_set_bcmcfg(cfg);
4210*4882a593Smuzhiyun
4211*4882a593Smuzhiyun return err;
4212*4882a593Smuzhiyun }
4213*4882a593Smuzhiyun
4214*4882a593Smuzhiyun #ifdef WL_SCHED_SCAN
4215*4882a593Smuzhiyun static s32
wl_cfgscan_init_pno_escan(struct bcm_cfg80211 * cfg,struct net_device * ndev,struct cfg80211_scan_request * request)4216*4882a593Smuzhiyun wl_cfgscan_init_pno_escan(struct bcm_cfg80211 *cfg, struct net_device *ndev,
4217*4882a593Smuzhiyun struct cfg80211_scan_request *request)
4218*4882a593Smuzhiyun {
4219*4882a593Smuzhiyun struct wiphy *wiphy = bcmcfg_to_wiphy(cfg);
4220*4882a593Smuzhiyun dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
4221*4882a593Smuzhiyun int err = 0;
4222*4882a593Smuzhiyun
4223*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
4224*4882a593Smuzhiyun LOG_TS(cfg, scan_start);
4225*4882a593Smuzhiyun
4226*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, SCANNING)) {
4227*4882a593Smuzhiyun _wl_cfgscan_cancel_scan(cfg);
4228*4882a593Smuzhiyun }
4229*4882a593Smuzhiyun
4230*4882a593Smuzhiyun wl_set_drv_status(cfg, SCANNING, ndev);
4231*4882a593Smuzhiyun WL_PNO((">>> Doing targeted ESCAN on PNO event\n"));
4232*4882a593Smuzhiyun
4233*4882a593Smuzhiyun err = wl_do_escan(cfg, wiphy, ndev, request);
4234*4882a593Smuzhiyun if (err) {
4235*4882a593Smuzhiyun wl_clr_drv_status(cfg, SCANNING, ndev);
4236*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
4237*4882a593Smuzhiyun WL_ERR(("targeted escan failed. err:%d\n", err));
4238*4882a593Smuzhiyun return err;
4239*4882a593Smuzhiyun }
4240*4882a593Smuzhiyun
4241*4882a593Smuzhiyun DBG_EVENT_LOG(dhdp, WIFI_EVENT_DRIVER_PNO_SCAN_REQUESTED);
4242*4882a593Smuzhiyun
4243*4882a593Smuzhiyun cfg->sched_scan_running = TRUE;
4244*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
4245*4882a593Smuzhiyun
4246*4882a593Smuzhiyun return err;
4247*4882a593Smuzhiyun }
4248*4882a593Smuzhiyun
4249*4882a593Smuzhiyun static s32
wl_cfgscan_update_v3_schedscan_results(struct bcm_cfg80211 * cfg,struct net_device * ndev,wl_pfn_scanresults_v3_t * pfn_result,uint32 event_type)4250*4882a593Smuzhiyun wl_cfgscan_update_v3_schedscan_results(struct bcm_cfg80211 *cfg, struct net_device *ndev,
4251*4882a593Smuzhiyun wl_pfn_scanresults_v3_t *pfn_result, uint32 event_type)
4252*4882a593Smuzhiyun {
4253*4882a593Smuzhiyun int err = 0;
4254*4882a593Smuzhiyun wl_pfn_net_info_v3_t *netinfo, *pnetinfo;
4255*4882a593Smuzhiyun struct cfg80211_scan_request *request = NULL;
4256*4882a593Smuzhiyun struct cfg80211_ssid ssid[MAX_PFN_LIST_COUNT];
4257*4882a593Smuzhiyun struct ieee80211_channel *channel = NULL;
4258*4882a593Smuzhiyun struct wiphy *wiphy = bcmcfg_to_wiphy(cfg);
4259*4882a593Smuzhiyun dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
4260*4882a593Smuzhiyun log_conn_event_t *event_data = NULL;
4261*4882a593Smuzhiyun tlv_log *tlv_data = NULL;
4262*4882a593Smuzhiyun u32 alloc_len = 0;
4263*4882a593Smuzhiyun int channel_req = 0;
4264*4882a593Smuzhiyun u32 payload_len;
4265*4882a593Smuzhiyun
4266*4882a593Smuzhiyun if (event_type == WLC_E_PFN_NET_LOST) {
4267*4882a593Smuzhiyun WL_PNO(("Do Nothing %d\n", event_type));
4268*4882a593Smuzhiyun return 0;
4269*4882a593Smuzhiyun }
4270*4882a593Smuzhiyun
4271*4882a593Smuzhiyun WL_INFORM_MEM(("PFN NET FOUND event. count:%d \n", pfn_result->count));
4272*4882a593Smuzhiyun
4273*4882a593Smuzhiyun pnetinfo = (wl_pfn_net_info_v3_t *)pfn_result->netinfo;
4274*4882a593Smuzhiyun if (pfn_result->count > 0) {
4275*4882a593Smuzhiyun int i;
4276*4882a593Smuzhiyun
4277*4882a593Smuzhiyun if (pfn_result->count > MAX_PFN_LIST_COUNT) {
4278*4882a593Smuzhiyun pfn_result->count = MAX_PFN_LIST_COUNT;
4279*4882a593Smuzhiyun }
4280*4882a593Smuzhiyun
4281*4882a593Smuzhiyun bzero(&ssid, sizeof(ssid));
4282*4882a593Smuzhiyun
4283*4882a593Smuzhiyun request = (struct cfg80211_scan_request *)MALLOCZ(cfg->osh,
4284*4882a593Smuzhiyun sizeof(*request) + sizeof(*request->channels) * pfn_result->count);
4285*4882a593Smuzhiyun channel = (struct ieee80211_channel *)MALLOCZ(cfg->osh,
4286*4882a593Smuzhiyun (sizeof(struct ieee80211_channel) * pfn_result->count));
4287*4882a593Smuzhiyun if (!request || !channel) {
4288*4882a593Smuzhiyun WL_ERR(("No memory"));
4289*4882a593Smuzhiyun err = -ENOMEM;
4290*4882a593Smuzhiyun goto out_err;
4291*4882a593Smuzhiyun }
4292*4882a593Smuzhiyun
4293*4882a593Smuzhiyun request->wiphy = wiphy;
4294*4882a593Smuzhiyun
4295*4882a593Smuzhiyun if (DBG_RING_ACTIVE(dhdp, DHD_EVENT_RING_ID)) {
4296*4882a593Smuzhiyun alloc_len = sizeof(log_conn_event_t) + (3 * sizeof(tlv_log)) +
4297*4882a593Smuzhiyun DOT11_MAX_SSID_LEN + sizeof(uint16) + sizeof(int16);
4298*4882a593Smuzhiyun event_data = (log_conn_event_t *)MALLOCZ(cfg->osh, alloc_len);
4299*4882a593Smuzhiyun if (!event_data) {
4300*4882a593Smuzhiyun WL_ERR(("%s: failed to allocate the log_conn_event_t with "
4301*4882a593Smuzhiyun "length(%d)\n", __func__, alloc_len));
4302*4882a593Smuzhiyun err = -ENOMEM;
4303*4882a593Smuzhiyun goto out_err;
4304*4882a593Smuzhiyun }
4305*4882a593Smuzhiyun }
4306*4882a593Smuzhiyun
4307*4882a593Smuzhiyun for (i = 0; i < pfn_result->count; i++) {
4308*4882a593Smuzhiyun u16 ssid_len;
4309*4882a593Smuzhiyun u8 ssid_buf[DOT11_MAX_SSID_LEN + 1] = {0};
4310*4882a593Smuzhiyun netinfo = &pnetinfo[i];
4311*4882a593Smuzhiyun
4312*4882a593Smuzhiyun /* PFN result doesn't have all the info which are required by the
4313*4882a593Smuzhiyun * supplicant. (For e.g IEs) Do a target Escan so that sched scan
4314*4882a593Smuzhiyun * results are reported via wl_inform_single_bss in the required
4315*4882a593Smuzhiyun * format. Escan does require the scan request in the form of
4316*4882a593Smuzhiyun * cfg80211_scan_request. For timebeing, create
4317*4882a593Smuzhiyun * cfg80211_scan_request one out of the received PNO event.
4318*4882a593Smuzhiyun */
4319*4882a593Smuzhiyun ssid[i].ssid_len = ssid_len = MIN(DOT11_MAX_SSID_LEN,
4320*4882a593Smuzhiyun netinfo->pfnsubnet.SSID_len);
4321*4882a593Smuzhiyun /* max ssid_len as in previous step DOT11_MAX_SSID_LEN is same
4322*4882a593Smuzhiyun * as DOT11_MAX_SSID_LEN = 32
4323*4882a593Smuzhiyun */
4324*4882a593Smuzhiyun (void)memcpy_s(ssid[i].ssid, IEEE80211_MAX_SSID_LEN,
4325*4882a593Smuzhiyun netinfo->pfnsubnet.u.SSID, ssid_len);
4326*4882a593Smuzhiyun request->n_ssids++;
4327*4882a593Smuzhiyun
4328*4882a593Smuzhiyun channel_req = netinfo->pfnsubnet.chanspec;
4329*4882a593Smuzhiyun channel[i].center_freq = wl_channel_to_frequency(
4330*4882a593Smuzhiyun wf_chspec_ctlchan(netinfo->pfnsubnet.chanspec),
4331*4882a593Smuzhiyun CHSPEC_BAND(netinfo->pfnsubnet.chanspec));
4332*4882a593Smuzhiyun channel[i].band =
4333*4882a593Smuzhiyun wl_get_nl80211_band(CHSPEC_BAND(netinfo->pfnsubnet.chanspec));
4334*4882a593Smuzhiyun channel[i].flags |= IEEE80211_CHAN_NO_HT40;
4335*4882a593Smuzhiyun request->channels[i] = &channel[i];
4336*4882a593Smuzhiyun request->n_channels++;
4337*4882a593Smuzhiyun
4338*4882a593Smuzhiyun (void)memcpy_s(ssid_buf, IEEE80211_MAX_SSID_LEN,
4339*4882a593Smuzhiyun ssid[i].ssid, ssid_len);
4340*4882a593Smuzhiyun ssid_buf[ssid_len] = '\0';
4341*4882a593Smuzhiyun WL_INFORM_MEM(("[PNO] SSID:%s chanspec:0x%x freq:%d band:%d\n",
4342*4882a593Smuzhiyun ssid_buf, netinfo->pfnsubnet.chanspec,
4343*4882a593Smuzhiyun channel[i].center_freq, channel[i].band));
4344*4882a593Smuzhiyun if (DBG_RING_ACTIVE(dhdp, DHD_EVENT_RING_ID)) {
4345*4882a593Smuzhiyun payload_len = sizeof(log_conn_event_t);
4346*4882a593Smuzhiyun event_data->event = WIFI_EVENT_DRIVER_PNO_NETWORK_FOUND;
4347*4882a593Smuzhiyun tlv_data = event_data->tlvs;
4348*4882a593Smuzhiyun
4349*4882a593Smuzhiyun /* ssid */
4350*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_SSID;
4351*4882a593Smuzhiyun tlv_data->len = netinfo->pfnsubnet.SSID_len;
4352*4882a593Smuzhiyun (void)memcpy_s(tlv_data->value, DOT11_MAX_SSID_LEN,
4353*4882a593Smuzhiyun ssid[i].ssid, ssid[i].ssid_len);
4354*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
4355*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
4356*4882a593Smuzhiyun
4357*4882a593Smuzhiyun /* channel */
4358*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_CHANNEL;
4359*4882a593Smuzhiyun tlv_data->len = sizeof(uint16);
4360*4882a593Smuzhiyun (void)memcpy_s(tlv_data->value, sizeof(uint16),
4361*4882a593Smuzhiyun &channel_req, sizeof(uint16));
4362*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
4363*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
4364*4882a593Smuzhiyun
4365*4882a593Smuzhiyun /* rssi */
4366*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_RSSI;
4367*4882a593Smuzhiyun tlv_data->len = sizeof(int16);
4368*4882a593Smuzhiyun (void)memcpy_s(tlv_data->value, sizeof(uint16),
4369*4882a593Smuzhiyun &netinfo->RSSI, sizeof(int16));
4370*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
4371*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
4372*4882a593Smuzhiyun
4373*4882a593Smuzhiyun dhd_os_push_push_ring_data(dhdp, DHD_EVENT_RING_ID,
4374*4882a593Smuzhiyun &event_data->event, payload_len);
4375*4882a593Smuzhiyun }
4376*4882a593Smuzhiyun }
4377*4882a593Smuzhiyun
4378*4882a593Smuzhiyun /* assign parsed ssid array */
4379*4882a593Smuzhiyun if (request->n_ssids)
4380*4882a593Smuzhiyun request->ssids = &ssid[0];
4381*4882a593Smuzhiyun
4382*4882a593Smuzhiyun if (wl_get_p2p_status(cfg, DISCOVERY_ON)) {
4383*4882a593Smuzhiyun WL_PNO((">>> P2P discovery was ON. Disabling it\n"));
4384*4882a593Smuzhiyun err = wl_cfgp2p_discover_enable_search(cfg, false);
4385*4882a593Smuzhiyun if (unlikely(err)) {
4386*4882a593Smuzhiyun wl_clr_drv_status(cfg, SCANNING, ndev);
4387*4882a593Smuzhiyun return err;
4388*4882a593Smuzhiyun }
4389*4882a593Smuzhiyun p2p_scan(cfg) = false;
4390*4882a593Smuzhiyun }
4391*4882a593Smuzhiyun
4392*4882a593Smuzhiyun err = wl_cfgscan_init_pno_escan(cfg, ndev, request);
4393*4882a593Smuzhiyun if (err) {
4394*4882a593Smuzhiyun goto out_err;
4395*4882a593Smuzhiyun }
4396*4882a593Smuzhiyun }
4397*4882a593Smuzhiyun else {
4398*4882a593Smuzhiyun WL_ERR(("FALSE PNO Event. (pfn_count == 0) \n"));
4399*4882a593Smuzhiyun }
4400*4882a593Smuzhiyun
4401*4882a593Smuzhiyun out_err:
4402*4882a593Smuzhiyun if (request) {
4403*4882a593Smuzhiyun MFREE(cfg->osh, request,
4404*4882a593Smuzhiyun sizeof(*request) + sizeof(*request->channels) * pfn_result->count);
4405*4882a593Smuzhiyun }
4406*4882a593Smuzhiyun if (channel) {
4407*4882a593Smuzhiyun MFREE(cfg->osh, channel,
4408*4882a593Smuzhiyun (sizeof(struct ieee80211_channel) * pfn_result->count));
4409*4882a593Smuzhiyun }
4410*4882a593Smuzhiyun
4411*4882a593Smuzhiyun if (event_data) {
4412*4882a593Smuzhiyun MFREE(cfg->osh, event_data, alloc_len);
4413*4882a593Smuzhiyun }
4414*4882a593Smuzhiyun
4415*4882a593Smuzhiyun return err;
4416*4882a593Smuzhiyun }
4417*4882a593Smuzhiyun /* If target scan is not reliable, set the below define to "1" to do a
4418*4882a593Smuzhiyun * full escan
4419*4882a593Smuzhiyun */
4420*4882a593Smuzhiyun static s32
wl_notify_sched_scan_results(struct bcm_cfg80211 * cfg,struct net_device * ndev,const wl_event_msg_t * e,void * data)4421*4882a593Smuzhiyun wl_notify_sched_scan_results(struct bcm_cfg80211 *cfg, struct net_device *ndev,
4422*4882a593Smuzhiyun const wl_event_msg_t *e, void *data)
4423*4882a593Smuzhiyun {
4424*4882a593Smuzhiyun wl_pfn_net_info_v1_t *netinfo, *pnetinfo;
4425*4882a593Smuzhiyun wl_pfn_net_info_v2_t *netinfo_v2, *pnetinfo_v2;
4426*4882a593Smuzhiyun struct wiphy *wiphy = bcmcfg_to_wiphy(cfg);
4427*4882a593Smuzhiyun dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
4428*4882a593Smuzhiyun int err = 0;
4429*4882a593Smuzhiyun struct cfg80211_scan_request *request = NULL;
4430*4882a593Smuzhiyun struct cfg80211_ssid ssid[MAX_PFN_LIST_COUNT];
4431*4882a593Smuzhiyun struct ieee80211_channel *channel = NULL;
4432*4882a593Smuzhiyun int channel_req = 0;
4433*4882a593Smuzhiyun int band = 0;
4434*4882a593Smuzhiyun wl_pfn_scanresults_v1_t *pfn_result_v1 = (wl_pfn_scanresults_v1_t *)data;
4435*4882a593Smuzhiyun wl_pfn_scanresults_v2_t *pfn_result_v2 = (wl_pfn_scanresults_v2_t *)data;
4436*4882a593Smuzhiyun wl_pfn_scanresults_v3_t *pfn_result_v3 = (wl_pfn_scanresults_v3_t *)data;
4437*4882a593Smuzhiyun int n_pfn_results = 0;
4438*4882a593Smuzhiyun log_conn_event_t *event_data = NULL;
4439*4882a593Smuzhiyun tlv_log *tlv_data = NULL;
4440*4882a593Smuzhiyun u32 alloc_len = 0;
4441*4882a593Smuzhiyun u32 payload_len;
4442*4882a593Smuzhiyun u8 tmp_buf[DOT11_MAX_SSID_LEN + 1];
4443*4882a593Smuzhiyun
4444*4882a593Smuzhiyun WL_DBG(("Enter\n"));
4445*4882a593Smuzhiyun
4446*4882a593Smuzhiyun /* These static asserts guarantee v1/v2 net_info and subnet_info are compatible
4447*4882a593Smuzhiyun * in size and SSID offset, allowing v1 to be used below except for the results
4448*4882a593Smuzhiyun * fields themselves (status, count, offset to netinfo).
4449*4882a593Smuzhiyun */
4450*4882a593Smuzhiyun STATIC_ASSERT(sizeof(wl_pfn_net_info_v1_t) == sizeof(wl_pfn_net_info_v2_t));
4451*4882a593Smuzhiyun STATIC_ASSERT(sizeof(wl_pfn_lnet_info_v1_t) == sizeof(wl_pfn_lnet_info_v2_t));
4452*4882a593Smuzhiyun STATIC_ASSERT(sizeof(wl_pfn_subnet_info_v1_t) == sizeof(wl_pfn_subnet_info_v2_t));
4453*4882a593Smuzhiyun STATIC_ASSERT(OFFSETOF(wl_pfn_subnet_info_v1_t, SSID) ==
4454*4882a593Smuzhiyun OFFSETOF(wl_pfn_subnet_info_v2_t, u.SSID));
4455*4882a593Smuzhiyun
4456*4882a593Smuzhiyun /* Extract the version-specific items */
4457*4882a593Smuzhiyun if (pfn_result_v1->version == PFN_SCANRESULT_VERSION_V1) {
4458*4882a593Smuzhiyun n_pfn_results = pfn_result_v1->count;
4459*4882a593Smuzhiyun pnetinfo = pfn_result_v1->netinfo;
4460*4882a593Smuzhiyun WL_INFORM_MEM(("PFN NET FOUND event. count:%d \n", n_pfn_results));
4461*4882a593Smuzhiyun
4462*4882a593Smuzhiyun if (n_pfn_results > 0) {
4463*4882a593Smuzhiyun int i;
4464*4882a593Smuzhiyun
4465*4882a593Smuzhiyun if (n_pfn_results > MAX_PFN_LIST_COUNT)
4466*4882a593Smuzhiyun n_pfn_results = MAX_PFN_LIST_COUNT;
4467*4882a593Smuzhiyun
4468*4882a593Smuzhiyun bzero(&ssid, sizeof(ssid));
4469*4882a593Smuzhiyun
4470*4882a593Smuzhiyun request = (struct cfg80211_scan_request *)MALLOCZ(cfg->osh,
4471*4882a593Smuzhiyun sizeof(*request) + sizeof(*request->channels) * n_pfn_results);
4472*4882a593Smuzhiyun channel = (struct ieee80211_channel *)MALLOCZ(cfg->osh,
4473*4882a593Smuzhiyun (sizeof(struct ieee80211_channel) * n_pfn_results));
4474*4882a593Smuzhiyun if (!request || !channel) {
4475*4882a593Smuzhiyun WL_ERR(("No memory"));
4476*4882a593Smuzhiyun err = -ENOMEM;
4477*4882a593Smuzhiyun goto out_err;
4478*4882a593Smuzhiyun }
4479*4882a593Smuzhiyun
4480*4882a593Smuzhiyun request->wiphy = wiphy;
4481*4882a593Smuzhiyun
4482*4882a593Smuzhiyun if (DBG_RING_ACTIVE(dhdp, DHD_EVENT_RING_ID)) {
4483*4882a593Smuzhiyun alloc_len = sizeof(log_conn_event_t) + (3 * sizeof(tlv_log)) +
4484*4882a593Smuzhiyun DOT11_MAX_SSID_LEN + sizeof(uint16) + sizeof(int16);
4485*4882a593Smuzhiyun event_data = (log_conn_event_t *)MALLOCZ(cfg->osh, alloc_len);
4486*4882a593Smuzhiyun if (!event_data) {
4487*4882a593Smuzhiyun WL_ERR(("%s: failed to allocate the log_conn_event_t with "
4488*4882a593Smuzhiyun "length(%d)\n", __func__, alloc_len));
4489*4882a593Smuzhiyun goto out_err;
4490*4882a593Smuzhiyun }
4491*4882a593Smuzhiyun }
4492*4882a593Smuzhiyun
4493*4882a593Smuzhiyun for (i = 0; i < n_pfn_results; i++) {
4494*4882a593Smuzhiyun netinfo = &pnetinfo[i];
4495*4882a593Smuzhiyun /* This looks useless, shouldn't Coverity complain? */
4496*4882a593Smuzhiyun if (!netinfo) {
4497*4882a593Smuzhiyun WL_ERR(("Invalid netinfo ptr. index:%d", i));
4498*4882a593Smuzhiyun err = -EINVAL;
4499*4882a593Smuzhiyun goto out_err;
4500*4882a593Smuzhiyun }
4501*4882a593Smuzhiyun if (netinfo->pfnsubnet.SSID_len > DOT11_MAX_SSID_LEN) {
4502*4882a593Smuzhiyun WL_ERR(("Wrong SSID length:%d\n",
4503*4882a593Smuzhiyun netinfo->pfnsubnet.SSID_len));
4504*4882a593Smuzhiyun err = -EINVAL;
4505*4882a593Smuzhiyun goto out_err;
4506*4882a593Smuzhiyun }
4507*4882a593Smuzhiyun /* In previous step max SSID_len limited to DOT11_MAX_SSID_LEN
4508*4882a593Smuzhiyun * and tmp_buf size is DOT11_MAX_SSID_LEN+1
4509*4882a593Smuzhiyun */
4510*4882a593Smuzhiyun (void)memcpy_s(tmp_buf, DOT11_MAX_SSID_LEN,
4511*4882a593Smuzhiyun netinfo->pfnsubnet.SSID, netinfo->pfnsubnet.SSID_len);
4512*4882a593Smuzhiyun tmp_buf[netinfo->pfnsubnet.SSID_len] = '\0';
4513*4882a593Smuzhiyun WL_PNO((">>> SSID:%s Channel:%d \n",
4514*4882a593Smuzhiyun tmp_buf, netinfo->pfnsubnet.channel));
4515*4882a593Smuzhiyun /* PFN result doesn't have all the info which are required by
4516*4882a593Smuzhiyun * the supplicant. (For e.g IEs) Do a target Escan so that
4517*4882a593Smuzhiyun * sched scan results are reported via wl_inform_single_bss in
4518*4882a593Smuzhiyun * the required format. Escan does require the scan request in
4519*4882a593Smuzhiyun * the form of cfg80211_scan_request. For timebeing, create
4520*4882a593Smuzhiyun * cfg80211_scan_request one out of the received PNO event.
4521*4882a593Smuzhiyun */
4522*4882a593Smuzhiyun
4523*4882a593Smuzhiyun ssid[i].ssid_len = netinfo->pfnsubnet.SSID_len;
4524*4882a593Smuzhiyun /* Returning void as ssid[i].ssid_len is limited to max of
4525*4882a593Smuzhiyun * DOT11_MAX_SSID_LEN
4526*4882a593Smuzhiyun */
4527*4882a593Smuzhiyun (void)memcpy_s(ssid[i].ssid, IEEE80211_MAX_SSID_LEN,
4528*4882a593Smuzhiyun netinfo->pfnsubnet.SSID, ssid[i].ssid_len);
4529*4882a593Smuzhiyun request->n_ssids++;
4530*4882a593Smuzhiyun
4531*4882a593Smuzhiyun channel_req = netinfo->pfnsubnet.channel;
4532*4882a593Smuzhiyun band = (channel_req <= CH_MAX_2G_CHANNEL) ? NL80211_BAND_2GHZ
4533*4882a593Smuzhiyun : NL80211_BAND_5GHZ;
4534*4882a593Smuzhiyun channel[i].center_freq =
4535*4882a593Smuzhiyun ieee80211_channel_to_frequency(channel_req, band);
4536*4882a593Smuzhiyun channel[i].band = band;
4537*4882a593Smuzhiyun channel[i].flags |= IEEE80211_CHAN_NO_HT40;
4538*4882a593Smuzhiyun request->channels[i] = &channel[i];
4539*4882a593Smuzhiyun request->n_channels++;
4540*4882a593Smuzhiyun
4541*4882a593Smuzhiyun if (DBG_RING_ACTIVE(dhdp, DHD_EVENT_RING_ID)) {
4542*4882a593Smuzhiyun payload_len = sizeof(log_conn_event_t);
4543*4882a593Smuzhiyun event_data->event = WIFI_EVENT_DRIVER_PNO_NETWORK_FOUND;
4544*4882a593Smuzhiyun tlv_data = event_data->tlvs;
4545*4882a593Smuzhiyun
4546*4882a593Smuzhiyun /* ssid */
4547*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_SSID;
4548*4882a593Smuzhiyun tlv_data->len = ssid[i].ssid_len;
4549*4882a593Smuzhiyun (void)memcpy_s(tlv_data->value, DOT11_MAX_SSID_LEN,
4550*4882a593Smuzhiyun ssid[i].ssid, ssid[i].ssid_len);
4551*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
4552*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
4553*4882a593Smuzhiyun
4554*4882a593Smuzhiyun /* channel */
4555*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_CHANNEL;
4556*4882a593Smuzhiyun tlv_data->len = sizeof(uint16);
4557*4882a593Smuzhiyun (void)memcpy_s(tlv_data->value, sizeof(uint16),
4558*4882a593Smuzhiyun &channel_req, sizeof(uint16));
4559*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
4560*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
4561*4882a593Smuzhiyun
4562*4882a593Smuzhiyun /* rssi */
4563*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_RSSI;
4564*4882a593Smuzhiyun tlv_data->len = sizeof(int16);
4565*4882a593Smuzhiyun (void)memcpy_s(tlv_data->value, sizeof(int16),
4566*4882a593Smuzhiyun &netinfo->RSSI, sizeof(int16));
4567*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
4568*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
4569*4882a593Smuzhiyun
4570*4882a593Smuzhiyun dhd_os_push_push_ring_data(dhdp, DHD_EVENT_RING_ID,
4571*4882a593Smuzhiyun &event_data->event, payload_len);
4572*4882a593Smuzhiyun }
4573*4882a593Smuzhiyun }
4574*4882a593Smuzhiyun
4575*4882a593Smuzhiyun /* assign parsed ssid array */
4576*4882a593Smuzhiyun if (request->n_ssids)
4577*4882a593Smuzhiyun request->ssids = &ssid[0];
4578*4882a593Smuzhiyun
4579*4882a593Smuzhiyun if (wl_get_p2p_status(cfg, DISCOVERY_ON)) {
4580*4882a593Smuzhiyun WL_PNO((">>> P2P discovery was ON. Disabling it\n"));
4581*4882a593Smuzhiyun err = wl_cfgp2p_discover_enable_search(cfg, false);
4582*4882a593Smuzhiyun if (unlikely(err)) {
4583*4882a593Smuzhiyun wl_clr_drv_status(cfg, SCANNING, ndev);
4584*4882a593Smuzhiyun goto out_err;
4585*4882a593Smuzhiyun }
4586*4882a593Smuzhiyun p2p_scan(cfg) = false;
4587*4882a593Smuzhiyun }
4588*4882a593Smuzhiyun err = wl_cfgscan_init_pno_escan(cfg, ndev, request);
4589*4882a593Smuzhiyun if (err) {
4590*4882a593Smuzhiyun goto out_err;
4591*4882a593Smuzhiyun }
4592*4882a593Smuzhiyun }
4593*4882a593Smuzhiyun else {
4594*4882a593Smuzhiyun WL_ERR(("FALSE PNO Event. (pfn_count == 0) \n"));
4595*4882a593Smuzhiyun }
4596*4882a593Smuzhiyun
4597*4882a593Smuzhiyun } else if (pfn_result_v2->version == PFN_SCANRESULT_VERSION_V2) {
4598*4882a593Smuzhiyun n_pfn_results = pfn_result_v2->count;
4599*4882a593Smuzhiyun pnetinfo_v2 = (wl_pfn_net_info_v2_t *)pfn_result_v2->netinfo;
4600*4882a593Smuzhiyun
4601*4882a593Smuzhiyun if (e->event_type == WLC_E_PFN_NET_LOST) {
4602*4882a593Smuzhiyun WL_PNO(("Do Nothing %d\n", e->event_type));
4603*4882a593Smuzhiyun return 0;
4604*4882a593Smuzhiyun }
4605*4882a593Smuzhiyun
4606*4882a593Smuzhiyun WL_INFORM_MEM(("PFN NET FOUND event. count:%d \n", n_pfn_results));
4607*4882a593Smuzhiyun
4608*4882a593Smuzhiyun if (n_pfn_results > 0) {
4609*4882a593Smuzhiyun int i;
4610*4882a593Smuzhiyun
4611*4882a593Smuzhiyun if (n_pfn_results > MAX_PFN_LIST_COUNT)
4612*4882a593Smuzhiyun n_pfn_results = MAX_PFN_LIST_COUNT;
4613*4882a593Smuzhiyun
4614*4882a593Smuzhiyun bzero(&ssid, sizeof(ssid));
4615*4882a593Smuzhiyun
4616*4882a593Smuzhiyun request = (struct cfg80211_scan_request *)MALLOCZ(cfg->osh,
4617*4882a593Smuzhiyun sizeof(*request) + sizeof(*request->channels) * n_pfn_results);
4618*4882a593Smuzhiyun channel = (struct ieee80211_channel *)MALLOCZ(cfg->osh,
4619*4882a593Smuzhiyun (sizeof(struct ieee80211_channel) * n_pfn_results));
4620*4882a593Smuzhiyun if (!request || !channel) {
4621*4882a593Smuzhiyun WL_ERR(("No memory"));
4622*4882a593Smuzhiyun err = -ENOMEM;
4623*4882a593Smuzhiyun goto out_err;
4624*4882a593Smuzhiyun }
4625*4882a593Smuzhiyun
4626*4882a593Smuzhiyun request->wiphy = wiphy;
4627*4882a593Smuzhiyun
4628*4882a593Smuzhiyun if (DBG_RING_ACTIVE(dhdp, DHD_EVENT_RING_ID)) {
4629*4882a593Smuzhiyun alloc_len = sizeof(log_conn_event_t) + (3 * sizeof(tlv_log)) +
4630*4882a593Smuzhiyun DOT11_MAX_SSID_LEN + sizeof(uint16) + sizeof(int16);
4631*4882a593Smuzhiyun event_data = (log_conn_event_t *)MALLOC(cfg->osh, alloc_len);
4632*4882a593Smuzhiyun if (!event_data) {
4633*4882a593Smuzhiyun WL_ERR(("%s: failed to allocate the log_conn_event_t with "
4634*4882a593Smuzhiyun "length(%d)\n", __func__, alloc_len));
4635*4882a593Smuzhiyun goto out_err;
4636*4882a593Smuzhiyun }
4637*4882a593Smuzhiyun }
4638*4882a593Smuzhiyun
4639*4882a593Smuzhiyun for (i = 0; i < n_pfn_results; i++) {
4640*4882a593Smuzhiyun netinfo_v2 = &pnetinfo_v2[i];
4641*4882a593Smuzhiyun /* This looks useless, shouldn't Coverity complain? */
4642*4882a593Smuzhiyun if (!netinfo_v2) {
4643*4882a593Smuzhiyun WL_ERR(("Invalid netinfo ptr. index:%d", i));
4644*4882a593Smuzhiyun err = -EINVAL;
4645*4882a593Smuzhiyun goto out_err;
4646*4882a593Smuzhiyun }
4647*4882a593Smuzhiyun WL_PNO((">>> SSID:%s Channel:%d \n",
4648*4882a593Smuzhiyun netinfo_v2->pfnsubnet.u.SSID,
4649*4882a593Smuzhiyun netinfo_v2->pfnsubnet.channel));
4650*4882a593Smuzhiyun /* PFN result doesn't have all the info which are required by the
4651*4882a593Smuzhiyun * supplicant. (For e.g IEs) Do a target Escan so that sched scan
4652*4882a593Smuzhiyun * results are reported via wl_inform_single_bss in the required
4653*4882a593Smuzhiyun * format. Escan does require the scan request in the form of
4654*4882a593Smuzhiyun * cfg80211_scan_request. For timebeing, create
4655*4882a593Smuzhiyun * cfg80211_scan_request one out of the received PNO event.
4656*4882a593Smuzhiyun */
4657*4882a593Smuzhiyun ssid[i].ssid_len = MIN(DOT11_MAX_SSID_LEN,
4658*4882a593Smuzhiyun netinfo_v2->pfnsubnet.SSID_len);
4659*4882a593Smuzhiyun /* max ssid_len as in previous step DOT11_MAX_SSID_LEN is same
4660*4882a593Smuzhiyun * as DOT11_MAX_SSID_LEN = 32
4661*4882a593Smuzhiyun */
4662*4882a593Smuzhiyun (void)memcpy_s(ssid[i].ssid, IEEE80211_MAX_SSID_LEN,
4663*4882a593Smuzhiyun netinfo_v2->pfnsubnet.u.SSID, ssid[i].ssid_len);
4664*4882a593Smuzhiyun request->n_ssids++;
4665*4882a593Smuzhiyun
4666*4882a593Smuzhiyun channel_req = netinfo_v2->pfnsubnet.channel;
4667*4882a593Smuzhiyun band = (channel_req <= CH_MAX_2G_CHANNEL) ? NL80211_BAND_2GHZ
4668*4882a593Smuzhiyun : NL80211_BAND_5GHZ;
4669*4882a593Smuzhiyun channel[i].center_freq =
4670*4882a593Smuzhiyun ieee80211_channel_to_frequency(channel_req, band);
4671*4882a593Smuzhiyun channel[i].band = band;
4672*4882a593Smuzhiyun channel[i].flags |= IEEE80211_CHAN_NO_HT40;
4673*4882a593Smuzhiyun request->channels[i] = &channel[i];
4674*4882a593Smuzhiyun request->n_channels++;
4675*4882a593Smuzhiyun
4676*4882a593Smuzhiyun if (DBG_RING_ACTIVE(dhdp, DHD_EVENT_RING_ID)) {
4677*4882a593Smuzhiyun payload_len = sizeof(log_conn_event_t);
4678*4882a593Smuzhiyun event_data->event = WIFI_EVENT_DRIVER_PNO_NETWORK_FOUND;
4679*4882a593Smuzhiyun tlv_data = event_data->tlvs;
4680*4882a593Smuzhiyun
4681*4882a593Smuzhiyun /* ssid */
4682*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_SSID;
4683*4882a593Smuzhiyun tlv_data->len = netinfo_v2->pfnsubnet.SSID_len;
4684*4882a593Smuzhiyun (void)memcpy_s(tlv_data->value, DOT11_MAX_SSID_LEN,
4685*4882a593Smuzhiyun ssid[i].ssid, ssid[i].ssid_len);
4686*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
4687*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
4688*4882a593Smuzhiyun
4689*4882a593Smuzhiyun /* channel */
4690*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_CHANNEL;
4691*4882a593Smuzhiyun tlv_data->len = sizeof(uint16);
4692*4882a593Smuzhiyun (void)memcpy_s(tlv_data->value, sizeof(uint16),
4693*4882a593Smuzhiyun &channel_req, sizeof(uint16));
4694*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
4695*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
4696*4882a593Smuzhiyun
4697*4882a593Smuzhiyun /* rssi */
4698*4882a593Smuzhiyun tlv_data->tag = WIFI_TAG_RSSI;
4699*4882a593Smuzhiyun tlv_data->len = sizeof(int16);
4700*4882a593Smuzhiyun (void)memcpy_s(tlv_data->value, sizeof(uint16),
4701*4882a593Smuzhiyun &netinfo_v2->RSSI, sizeof(int16));
4702*4882a593Smuzhiyun payload_len += TLV_LOG_SIZE(tlv_data);
4703*4882a593Smuzhiyun tlv_data = TLV_LOG_NEXT(tlv_data);
4704*4882a593Smuzhiyun
4705*4882a593Smuzhiyun dhd_os_push_push_ring_data(dhdp, DHD_EVENT_RING_ID,
4706*4882a593Smuzhiyun &event_data->event, payload_len);
4707*4882a593Smuzhiyun }
4708*4882a593Smuzhiyun }
4709*4882a593Smuzhiyun
4710*4882a593Smuzhiyun /* assign parsed ssid array */
4711*4882a593Smuzhiyun if (request->n_ssids)
4712*4882a593Smuzhiyun request->ssids = &ssid[0];
4713*4882a593Smuzhiyun
4714*4882a593Smuzhiyun if (wl_get_p2p_status(cfg, DISCOVERY_ON)) {
4715*4882a593Smuzhiyun WL_PNO((">>> P2P discovery was ON. Disabling it\n"));
4716*4882a593Smuzhiyun err = wl_cfgp2p_discover_enable_search(cfg, false);
4717*4882a593Smuzhiyun if (unlikely(err)) {
4718*4882a593Smuzhiyun wl_clr_drv_status(cfg, SCANNING, ndev);
4719*4882a593Smuzhiyun goto out_err;
4720*4882a593Smuzhiyun }
4721*4882a593Smuzhiyun p2p_scan(cfg) = false;
4722*4882a593Smuzhiyun }
4723*4882a593Smuzhiyun
4724*4882a593Smuzhiyun err = wl_cfgscan_init_pno_escan(cfg, ndev, request);
4725*4882a593Smuzhiyun if (err) {
4726*4882a593Smuzhiyun goto out_err;
4727*4882a593Smuzhiyun }
4728*4882a593Smuzhiyun }
4729*4882a593Smuzhiyun else {
4730*4882a593Smuzhiyun WL_ERR(("FALSE PNO Event. (pfn_count == 0) \n"));
4731*4882a593Smuzhiyun }
4732*4882a593Smuzhiyun } else if (pfn_result_v3->version == PFN_SCANRESULT_VERSION_V3) {
4733*4882a593Smuzhiyun err = wl_cfgscan_update_v3_schedscan_results(cfg, ndev,
4734*4882a593Smuzhiyun pfn_result_v3, e->event_type);
4735*4882a593Smuzhiyun if (err) {
4736*4882a593Smuzhiyun goto out_err;
4737*4882a593Smuzhiyun }
4738*4882a593Smuzhiyun } else {
4739*4882a593Smuzhiyun WL_ERR(("Unsupported version %d, expected %d or %d\n", pfn_result_v1->version,
4740*4882a593Smuzhiyun PFN_SCANRESULT_VERSION_V1, PFN_SCANRESULT_VERSION_V2));
4741*4882a593Smuzhiyun err = -EINVAL;
4742*4882a593Smuzhiyun }
4743*4882a593Smuzhiyun
4744*4882a593Smuzhiyun out_err:
4745*4882a593Smuzhiyun
4746*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
4747*4882a593Smuzhiyun if (err) {
4748*4882a593Smuzhiyun /* Notify upper layer that sched scan has stopped so that
4749*4882a593Smuzhiyun * upper layer can attempt fresh scan.
4750*4882a593Smuzhiyun */
4751*4882a593Smuzhiyun if (cfg->sched_scan_req) {
4752*4882a593Smuzhiyun WL_ERR(("sched_scan stopped\n"));
4753*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0))
4754*4882a593Smuzhiyun cfg80211_sched_scan_stopped(wiphy, cfg->sched_scan_req->reqid);
4755*4882a593Smuzhiyun #else
4756*4882a593Smuzhiyun cfg80211_sched_scan_stopped(wiphy);
4757*4882a593Smuzhiyun #endif /* KERNEL > 4.11.0 */
4758*4882a593Smuzhiyun cfg->sched_scan_req = NULL;
4759*4882a593Smuzhiyun } else {
4760*4882a593Smuzhiyun WL_ERR(("sched scan req null!\n"));
4761*4882a593Smuzhiyun }
4762*4882a593Smuzhiyun cfg->sched_scan_running = FALSE;
4763*4882a593Smuzhiyun CLR_TS(cfg, scan_start);
4764*4882a593Smuzhiyun }
4765*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
4766*4882a593Smuzhiyun
4767*4882a593Smuzhiyun if (request) {
4768*4882a593Smuzhiyun MFREE(cfg->osh, request,
4769*4882a593Smuzhiyun sizeof(*request) + sizeof(*request->channels) * n_pfn_results);
4770*4882a593Smuzhiyun }
4771*4882a593Smuzhiyun if (channel) {
4772*4882a593Smuzhiyun MFREE(cfg->osh, channel,
4773*4882a593Smuzhiyun (sizeof(struct ieee80211_channel) * n_pfn_results));
4774*4882a593Smuzhiyun }
4775*4882a593Smuzhiyun
4776*4882a593Smuzhiyun if (event_data) {
4777*4882a593Smuzhiyun MFREE(cfg->osh, event_data, alloc_len);
4778*4882a593Smuzhiyun }
4779*4882a593Smuzhiyun return err;
4780*4882a593Smuzhiyun }
4781*4882a593Smuzhiyun #endif /* WL_SCHED_SCAN */
4782*4882a593Smuzhiyun
4783*4882a593Smuzhiyun #ifdef PNO_SUPPORT
4784*4882a593Smuzhiyun s32
wl_notify_pfn_status(struct bcm_cfg80211 * cfg,bcm_struct_cfgdev * cfgdev,const wl_event_msg_t * e,void * data)4785*4882a593Smuzhiyun wl_notify_pfn_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
4786*4882a593Smuzhiyun const wl_event_msg_t *e, void *data)
4787*4882a593Smuzhiyun {
4788*4882a593Smuzhiyun struct net_device *ndev = NULL;
4789*4882a593Smuzhiyun #ifdef GSCAN_SUPPORT
4790*4882a593Smuzhiyun void *ptr;
4791*4882a593Smuzhiyun int send_evt_bytes = 0;
4792*4882a593Smuzhiyun u32 event = be32_to_cpu(e->event_type);
4793*4882a593Smuzhiyun struct wiphy *wiphy = bcmcfg_to_wiphy(cfg);
4794*4882a593Smuzhiyun #endif /* GSCAN_SUPPORT */
4795*4882a593Smuzhiyun
4796*4882a593Smuzhiyun WL_INFORM_MEM((">>> PNO Event\n"));
4797*4882a593Smuzhiyun
4798*4882a593Smuzhiyun if (!data) {
4799*4882a593Smuzhiyun WL_ERR(("Data received is NULL!\n"));
4800*4882a593Smuzhiyun return 0;
4801*4882a593Smuzhiyun }
4802*4882a593Smuzhiyun
4803*4882a593Smuzhiyun ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);
4804*4882a593Smuzhiyun #ifdef GSCAN_SUPPORT
4805*4882a593Smuzhiyun ptr = dhd_dev_process_epno_result(ndev, data, event, &send_evt_bytes);
4806*4882a593Smuzhiyun if (ptr) {
4807*4882a593Smuzhiyun wl_cfgvendor_send_async_event(wiphy, ndev,
4808*4882a593Smuzhiyun GOOGLE_SCAN_EPNO_EVENT, ptr, send_evt_bytes);
4809*4882a593Smuzhiyun MFREE(cfg->osh, ptr, send_evt_bytes);
4810*4882a593Smuzhiyun }
4811*4882a593Smuzhiyun if (!dhd_dev_is_legacy_pno_enabled(ndev))
4812*4882a593Smuzhiyun return 0;
4813*4882a593Smuzhiyun #endif /* GSCAN_SUPPORT */
4814*4882a593Smuzhiyun
4815*4882a593Smuzhiyun #ifndef WL_SCHED_SCAN
4816*4882a593Smuzhiyun /* CUSTOMER_HW4 has other PNO wakelock time by RB:5911 */
4817*4882a593Smuzhiyun mutex_lock(&cfg->usr_sync);
4818*4882a593Smuzhiyun /* TODO: Use cfg80211_sched_scan_results(wiphy); */
4819*4882a593Smuzhiyun /* GregG : WAR as to supplicant busy and not allowed Kernel to suspend */
4820*4882a593Smuzhiyun CFG80211_DISCONNECTED(ndev, 0, NULL, 0, false, GFP_KERNEL);
4821*4882a593Smuzhiyun mutex_unlock(&cfg->usr_sync);
4822*4882a593Smuzhiyun #else
4823*4882a593Smuzhiyun /* If cfg80211 scheduled scan is supported, report the pno results via sched
4824*4882a593Smuzhiyun * scan results
4825*4882a593Smuzhiyun */
4826*4882a593Smuzhiyun wl_notify_sched_scan_results(cfg, ndev, e, data);
4827*4882a593Smuzhiyun #endif /* WL_SCHED_SCAN */
4828*4882a593Smuzhiyun return 0;
4829*4882a593Smuzhiyun }
4830*4882a593Smuzhiyun #endif /* PNO_SUPPORT */
4831*4882a593Smuzhiyun
4832*4882a593Smuzhiyun #ifdef GSCAN_SUPPORT
4833*4882a593Smuzhiyun s32
wl_notify_gscan_event(struct bcm_cfg80211 * cfg,bcm_struct_cfgdev * cfgdev,const wl_event_msg_t * e,void * data)4834*4882a593Smuzhiyun wl_notify_gscan_event(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
4835*4882a593Smuzhiyun const wl_event_msg_t *e, void *data)
4836*4882a593Smuzhiyun {
4837*4882a593Smuzhiyun s32 err = 0;
4838*4882a593Smuzhiyun u32 event = be32_to_cpu(e->event_type);
4839*4882a593Smuzhiyun void *ptr = NULL;
4840*4882a593Smuzhiyun int send_evt_bytes = 0;
4841*4882a593Smuzhiyun int event_type;
4842*4882a593Smuzhiyun struct net_device *ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);
4843*4882a593Smuzhiyun struct wiphy *wiphy = bcmcfg_to_wiphy(cfg);
4844*4882a593Smuzhiyun u32 len = ntoh32(e->datalen);
4845*4882a593Smuzhiyun u32 buf_len = 0;
4846*4882a593Smuzhiyun
4847*4882a593Smuzhiyun switch (event) {
4848*4882a593Smuzhiyun case WLC_E_PFN_BEST_BATCHING:
4849*4882a593Smuzhiyun err = dhd_dev_retrieve_batch_scan(ndev);
4850*4882a593Smuzhiyun if (err < 0) {
4851*4882a593Smuzhiyun WL_ERR(("Batch retrieval already in progress %d\n", err));
4852*4882a593Smuzhiyun } else {
4853*4882a593Smuzhiyun event_type = WIFI_SCAN_THRESHOLD_NUM_SCANS;
4854*4882a593Smuzhiyun if (data && len) {
4855*4882a593Smuzhiyun event_type = *((int *)data);
4856*4882a593Smuzhiyun }
4857*4882a593Smuzhiyun wl_cfgvendor_send_async_event(wiphy, ndev,
4858*4882a593Smuzhiyun GOOGLE_GSCAN_BATCH_SCAN_EVENT,
4859*4882a593Smuzhiyun &event_type, sizeof(int));
4860*4882a593Smuzhiyun }
4861*4882a593Smuzhiyun break;
4862*4882a593Smuzhiyun case WLC_E_PFN_SCAN_COMPLETE:
4863*4882a593Smuzhiyun event_type = WIFI_SCAN_COMPLETE;
4864*4882a593Smuzhiyun wl_cfgvendor_send_async_event(wiphy, ndev,
4865*4882a593Smuzhiyun GOOGLE_SCAN_COMPLETE_EVENT,
4866*4882a593Smuzhiyun &event_type, sizeof(int));
4867*4882a593Smuzhiyun break;
4868*4882a593Smuzhiyun case WLC_E_PFN_BSSID_NET_FOUND:
4869*4882a593Smuzhiyun ptr = dhd_dev_hotlist_scan_event(ndev, data, &send_evt_bytes,
4870*4882a593Smuzhiyun HOTLIST_FOUND, &buf_len);
4871*4882a593Smuzhiyun if (ptr) {
4872*4882a593Smuzhiyun wl_cfgvendor_send_hotlist_event(wiphy, ndev,
4873*4882a593Smuzhiyun ptr, send_evt_bytes, GOOGLE_GSCAN_GEOFENCE_FOUND_EVENT);
4874*4882a593Smuzhiyun dhd_dev_gscan_hotlist_cache_cleanup(ndev, HOTLIST_FOUND);
4875*4882a593Smuzhiyun MFREE(cfg->osh, ptr, send_evt_bytes);
4876*4882a593Smuzhiyun } else {
4877*4882a593Smuzhiyun err = -ENOMEM;
4878*4882a593Smuzhiyun }
4879*4882a593Smuzhiyun break;
4880*4882a593Smuzhiyun case WLC_E_PFN_BSSID_NET_LOST:
4881*4882a593Smuzhiyun /* WLC_E_PFN_BSSID_NET_LOST is conflict shared with WLC_E_PFN_SCAN_ALLGONE
4882*4882a593Smuzhiyun * We currently do not use WLC_E_PFN_SCAN_ALLGONE, so if we get it, ignore
4883*4882a593Smuzhiyun */
4884*4882a593Smuzhiyun if (len) {
4885*4882a593Smuzhiyun ptr = dhd_dev_hotlist_scan_event(ndev, data, &send_evt_bytes,
4886*4882a593Smuzhiyun HOTLIST_LOST, &buf_len);
4887*4882a593Smuzhiyun if (ptr) {
4888*4882a593Smuzhiyun wl_cfgvendor_send_hotlist_event(wiphy, ndev,
4889*4882a593Smuzhiyun ptr, send_evt_bytes, GOOGLE_GSCAN_GEOFENCE_LOST_EVENT);
4890*4882a593Smuzhiyun dhd_dev_gscan_hotlist_cache_cleanup(ndev, HOTLIST_LOST);
4891*4882a593Smuzhiyun MFREE(cfg->osh, ptr, send_evt_bytes);
4892*4882a593Smuzhiyun } else {
4893*4882a593Smuzhiyun err = -ENOMEM;
4894*4882a593Smuzhiyun }
4895*4882a593Smuzhiyun } else {
4896*4882a593Smuzhiyun err = -EINVAL;
4897*4882a593Smuzhiyun }
4898*4882a593Smuzhiyun break;
4899*4882a593Smuzhiyun case WLC_E_PFN_GSCAN_FULL_RESULT:
4900*4882a593Smuzhiyun ptr = dhd_dev_process_full_gscan_result(ndev, data, len, &send_evt_bytes);
4901*4882a593Smuzhiyun if (ptr) {
4902*4882a593Smuzhiyun wl_cfgvendor_send_async_event(wiphy, ndev,
4903*4882a593Smuzhiyun GOOGLE_SCAN_FULL_RESULTS_EVENT, ptr, send_evt_bytes);
4904*4882a593Smuzhiyun MFREE(cfg->osh, ptr, send_evt_bytes);
4905*4882a593Smuzhiyun } else {
4906*4882a593Smuzhiyun err = -ENOMEM;
4907*4882a593Smuzhiyun }
4908*4882a593Smuzhiyun break;
4909*4882a593Smuzhiyun case WLC_E_PFN_SSID_EXT:
4910*4882a593Smuzhiyun ptr = dhd_dev_process_epno_result(ndev, data, event, &send_evt_bytes);
4911*4882a593Smuzhiyun if (ptr) {
4912*4882a593Smuzhiyun wl_cfgvendor_send_async_event(wiphy, ndev,
4913*4882a593Smuzhiyun GOOGLE_SCAN_EPNO_EVENT, ptr, send_evt_bytes);
4914*4882a593Smuzhiyun MFREE(cfg->osh, ptr, send_evt_bytes);
4915*4882a593Smuzhiyun } else {
4916*4882a593Smuzhiyun err = -ENOMEM;
4917*4882a593Smuzhiyun }
4918*4882a593Smuzhiyun break;
4919*4882a593Smuzhiyun default:
4920*4882a593Smuzhiyun WL_ERR(("Unknown event %d\n", event));
4921*4882a593Smuzhiyun break;
4922*4882a593Smuzhiyun }
4923*4882a593Smuzhiyun return err;
4924*4882a593Smuzhiyun }
4925*4882a593Smuzhiyun #endif /* GSCAN_SUPPORT */
4926*4882a593Smuzhiyun
wl_cfg80211_set_passive_scan(struct net_device * dev,char * command)4927*4882a593Smuzhiyun void wl_cfg80211_set_passive_scan(struct net_device *dev, char *command)
4928*4882a593Smuzhiyun {
4929*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
4930*4882a593Smuzhiyun
4931*4882a593Smuzhiyun if (strcmp(command, "SCAN-ACTIVE") == 0) {
4932*4882a593Smuzhiyun cfg->active_scan = 1;
4933*4882a593Smuzhiyun } else if (strcmp(command, "SCAN-PASSIVE") == 0) {
4934*4882a593Smuzhiyun cfg->active_scan = 0;
4935*4882a593Smuzhiyun } else
4936*4882a593Smuzhiyun WL_ERR(("Unknown command \n"));
4937*4882a593Smuzhiyun return;
4938*4882a593Smuzhiyun }
4939*4882a593Smuzhiyun
4940*4882a593Smuzhiyun void
wl_cfgscan_listen_complete_work(struct work_struct * work)4941*4882a593Smuzhiyun wl_cfgscan_listen_complete_work(struct work_struct *work)
4942*4882a593Smuzhiyun {
4943*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = NULL;
4944*4882a593Smuzhiyun BCM_SET_CONTAINER_OF(cfg, work, struct bcm_cfg80211, loc.work.work);
4945*4882a593Smuzhiyun
4946*4882a593Smuzhiyun WL_ERR(("listen timeout\n"));
4947*4882a593Smuzhiyun /* listen not completed. Do recovery */
4948*4882a593Smuzhiyun if (!cfg->loc.in_progress) {
4949*4882a593Smuzhiyun WL_ERR(("No listen in progress!\n"));
4950*4882a593Smuzhiyun return;
4951*4882a593Smuzhiyun }
4952*4882a593Smuzhiyun wl_cfgscan_notify_listen_complete(cfg);
4953*4882a593Smuzhiyun }
4954*4882a593Smuzhiyun
4955*4882a593Smuzhiyun s32
wl_cfgscan_notify_listen_complete(struct bcm_cfg80211 * cfg)4956*4882a593Smuzhiyun wl_cfgscan_notify_listen_complete(struct bcm_cfg80211 *cfg)
4957*4882a593Smuzhiyun {
4958*4882a593Smuzhiyun WL_DBG(("listen on channel complete! cookie:%llu\n", cfg->last_roc_id));
4959*4882a593Smuzhiyun if (cfg->loc.wdev && cfg->loc.in_progress) {
4960*4882a593Smuzhiyun #if defined(WL_CFG80211_P2P_DEV_IF)
4961*4882a593Smuzhiyun cfg80211_remain_on_channel_expired(cfg->loc.wdev, cfg->last_roc_id,
4962*4882a593Smuzhiyun &cfg->remain_on_chan, GFP_KERNEL);
4963*4882a593Smuzhiyun #else
4964*4882a593Smuzhiyun cfg80211_remain_on_channel_expired(cfg->loc.wdev->netdev, cfg->last_roc_id,
4965*4882a593Smuzhiyun &cfg->remain_on_chan, cfg->remain_on_chan_type, GFP_KERNEL);
4966*4882a593Smuzhiyun #endif
4967*4882a593Smuzhiyun cfg->loc.in_progress = false;
4968*4882a593Smuzhiyun cfg->loc.wdev = NULL;
4969*4882a593Smuzhiyun }
4970*4882a593Smuzhiyun return BCME_OK;
4971*4882a593Smuzhiyun }
4972*4882a593Smuzhiyun
4973*4882a593Smuzhiyun static void
wl_init_scan_params(struct bcm_cfg80211 * cfg,u8 * params,u16 params_size,u32 scan_type,u32 action,u32 passive_time)4974*4882a593Smuzhiyun wl_init_scan_params(struct bcm_cfg80211 *cfg, u8 *params, u16 params_size,
4975*4882a593Smuzhiyun u32 scan_type, u32 action, u32 passive_time)
4976*4882a593Smuzhiyun {
4977*4882a593Smuzhiyun u32 sync_id = 0;
4978*4882a593Smuzhiyun wl_escan_params_t *eparams = NULL;
4979*4882a593Smuzhiyun wl_escan_params_v2_t *eparams_v2 = NULL;
4980*4882a593Smuzhiyun wl_scan_params_t *scanparams = NULL;
4981*4882a593Smuzhiyun wl_scan_params_v2_t *scanparams_v2 = NULL;
4982*4882a593Smuzhiyun
4983*4882a593Smuzhiyun wl_escan_set_sync_id(sync_id, cfg);
4984*4882a593Smuzhiyun if (cfg->scan_params_v2) {
4985*4882a593Smuzhiyun eparams_v2 = (wl_escan_params_v2_t *)params;
4986*4882a593Smuzhiyun eparams_v2->version = htod32(ESCAN_REQ_VERSION_V2);
4987*4882a593Smuzhiyun eparams_v2->action = htod16(action);
4988*4882a593Smuzhiyun eparams_v2->sync_id = sync_id;
4989*4882a593Smuzhiyun scanparams_v2 = (wl_scan_params_v2_t *)&eparams_v2->params;
4990*4882a593Smuzhiyun (void)memcpy_s(&scanparams_v2->bssid, ETHER_ADDR_LEN, ðer_bcast, ETHER_ADDR_LEN);
4991*4882a593Smuzhiyun scanparams_v2->version = htod16(WL_SCAN_PARAMS_VERSION_V2);
4992*4882a593Smuzhiyun scanparams_v2->length = htod16(sizeof(wl_scan_params_v2_t));
4993*4882a593Smuzhiyun scanparams_v2->bss_type = DOT11_BSSTYPE_ANY;
4994*4882a593Smuzhiyun scanparams_v2->scan_type = htod32(scan_type);
4995*4882a593Smuzhiyun scanparams_v2->nprobes = htod32(-1);
4996*4882a593Smuzhiyun scanparams_v2->active_time = htod32(-1);
4997*4882a593Smuzhiyun scanparams_v2->passive_time = htod32(passive_time);
4998*4882a593Smuzhiyun scanparams_v2->home_time = htod32(-1);
4999*4882a593Smuzhiyun bzero(&scanparams_v2->ssid, sizeof(wlc_ssid_t));
5000*4882a593Smuzhiyun } else {
5001*4882a593Smuzhiyun eparams = (wl_escan_params_t *)params;
5002*4882a593Smuzhiyun eparams->version = htod32(ESCAN_REQ_VERSION);
5003*4882a593Smuzhiyun eparams->action = htod16(action);
5004*4882a593Smuzhiyun eparams->sync_id = sync_id;
5005*4882a593Smuzhiyun scanparams = (wl_scan_params_t *)&eparams->params;
5006*4882a593Smuzhiyun (void)memcpy_s(&scanparams->bssid, ETHER_ADDR_LEN, ðer_bcast, ETHER_ADDR_LEN);
5007*4882a593Smuzhiyun scanparams->bss_type = DOT11_BSSTYPE_ANY;
5008*4882a593Smuzhiyun scanparams->scan_type = 0;
5009*4882a593Smuzhiyun scanparams->nprobes = htod32(-1);
5010*4882a593Smuzhiyun scanparams->active_time = htod32(-1);
5011*4882a593Smuzhiyun scanparams->passive_time = htod32(passive_time);
5012*4882a593Smuzhiyun scanparams->home_time = htod32(-1);
5013*4882a593Smuzhiyun bzero(&scanparams->ssid, sizeof(wlc_ssid_t));
5014*4882a593Smuzhiyun }
5015*4882a593Smuzhiyun }
5016*4882a593Smuzhiyun
5017*4882a593Smuzhiyun /* timeout for recoverying upper layer statemachine */
5018*4882a593Smuzhiyun #define WL_LISTEN_TIMEOUT 3000u
5019*4882a593Smuzhiyun
5020*4882a593Smuzhiyun s32
wl_cfgscan_cancel_listen_on_channel(struct bcm_cfg80211 * cfg,bool notify_user)5021*4882a593Smuzhiyun wl_cfgscan_cancel_listen_on_channel(struct bcm_cfg80211 *cfg, bool notify_user)
5022*4882a593Smuzhiyun {
5023*4882a593Smuzhiyun WL_DBG(("Enter\n"));
5024*4882a593Smuzhiyun
5025*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
5026*4882a593Smuzhiyun if (!cfg->loc.in_progress) {
5027*4882a593Smuzhiyun WL_ERR(("listen not in progress. do nothing\n"));
5028*4882a593Smuzhiyun goto exit;
5029*4882a593Smuzhiyun }
5030*4882a593Smuzhiyun
5031*4882a593Smuzhiyun if (delayed_work_pending(&cfg->loc.work)) {
5032*4882a593Smuzhiyun cancel_delayed_work_sync(&cfg->loc.work);
5033*4882a593Smuzhiyun }
5034*4882a593Smuzhiyun
5035*4882a593Smuzhiyun /* abort scan listen */
5036*4882a593Smuzhiyun _wl_cfgscan_cancel_scan(cfg);
5037*4882a593Smuzhiyun
5038*4882a593Smuzhiyun if (notify_user) {
5039*4882a593Smuzhiyun wl_cfgscan_notify_listen_complete(cfg);
5040*4882a593Smuzhiyun }
5041*4882a593Smuzhiyun cfg->loc.in_progress = false;
5042*4882a593Smuzhiyun cfg->loc.wdev = NULL;
5043*4882a593Smuzhiyun exit:
5044*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
5045*4882a593Smuzhiyun return 0;
5046*4882a593Smuzhiyun }
5047*4882a593Smuzhiyun
5048*4882a593Smuzhiyun s32
wl_cfgscan_listen_on_channel(struct bcm_cfg80211 * cfg,struct wireless_dev * wdev,struct ieee80211_channel * channel,unsigned int duration)5049*4882a593Smuzhiyun wl_cfgscan_listen_on_channel(struct bcm_cfg80211 *cfg, struct wireless_dev *wdev,
5050*4882a593Smuzhiyun struct ieee80211_channel *channel, unsigned int duration)
5051*4882a593Smuzhiyun {
5052*4882a593Smuzhiyun u32 dwell = duration;
5053*4882a593Smuzhiyun u32 chanspec, params_size;
5054*4882a593Smuzhiyun u16 chanspec_num = 0;
5055*4882a593Smuzhiyun s32 bssidx = -1;
5056*4882a593Smuzhiyun s32 err = 0;
5057*4882a593Smuzhiyun struct net_device *ndev = NULL;
5058*4882a593Smuzhiyun u8 *params = NULL;
5059*4882a593Smuzhiyun wl_escan_params_t *eparams = NULL;
5060*4882a593Smuzhiyun wl_escan_params_v2_t *eparams_v2 = NULL;
5061*4882a593Smuzhiyun wl_scan_params_t *scanparams = NULL;
5062*4882a593Smuzhiyun wl_scan_params_v2_t *scanparams_v2 = NULL;
5063*4882a593Smuzhiyun u16 *chanspec_list = NULL;
5064*4882a593Smuzhiyun u32 channel_num = 0, scan_type = 0;
5065*4882a593Smuzhiyun
5066*4882a593Smuzhiyun WL_DBG(("Enter \n"));
5067*4882a593Smuzhiyun if (!wdev) {
5068*4882a593Smuzhiyun WL_ERR(("wdev null!\n"));
5069*4882a593Smuzhiyun return -EINVAL;
5070*4882a593Smuzhiyun }
5071*4882a593Smuzhiyun
5072*4882a593Smuzhiyun mutex_lock(&cfg->scan_sync);
5073*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, SCANNING)) {
5074*4882a593Smuzhiyun WL_ERR(("Scanning in progress avoid listen on channel\n"));
5075*4882a593Smuzhiyun err = -EBUSY;
5076*4882a593Smuzhiyun goto exit;
5077*4882a593Smuzhiyun }
5078*4882a593Smuzhiyun if (cfg->loc.in_progress == true) {
5079*4882a593Smuzhiyun WL_ERR(("Listen in progress\n"));
5080*4882a593Smuzhiyun err = -EAGAIN;
5081*4882a593Smuzhiyun goto exit;
5082*4882a593Smuzhiyun }
5083*4882a593Smuzhiyun bssidx = wl_get_bssidx_by_wdev(cfg, wdev);
5084*4882a593Smuzhiyun if (bssidx < 0) {
5085*4882a593Smuzhiyun WL_ERR(("invalid bssidx!\n"));
5086*4882a593Smuzhiyun err = -EINVAL;
5087*4882a593Smuzhiyun goto exit;
5088*4882a593Smuzhiyun }
5089*4882a593Smuzhiyun
5090*4882a593Smuzhiyun /* Use primary ndev for netless dev. BSSIDX will point to right I/F */
5091*4882a593Smuzhiyun ndev = wdev->netdev ? wdev->netdev : bcmcfg_to_prmry_ndev(cfg);
5092*4882a593Smuzhiyun
5093*4882a593Smuzhiyun if (cfg->scan_params_v2) {
5094*4882a593Smuzhiyun params_size = (WL_SCAN_PARAMS_V2_FIXED_SIZE +
5095*4882a593Smuzhiyun OFFSETOF(wl_escan_params_v2_t, params));
5096*4882a593Smuzhiyun } else {
5097*4882a593Smuzhiyun params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params));
5098*4882a593Smuzhiyun }
5099*4882a593Smuzhiyun
5100*4882a593Smuzhiyun /* Single channel for listen case. Add a padd of u16 for alignment */
5101*4882a593Smuzhiyun chanspec_num = 1;
5102*4882a593Smuzhiyun params_size += (chanspec_num + 1);
5103*4882a593Smuzhiyun
5104*4882a593Smuzhiyun /* Allocate space for populating single ssid in wl_escan_params_t struct */
5105*4882a593Smuzhiyun params_size += ((u32) sizeof(struct wlc_ssid));
5106*4882a593Smuzhiyun
5107*4882a593Smuzhiyun params = MALLOCZ(cfg->osh, params_size);
5108*4882a593Smuzhiyun if (params == NULL) {
5109*4882a593Smuzhiyun err = -ENOMEM;
5110*4882a593Smuzhiyun WL_ERR(("listen fail. no mem.\n"));
5111*4882a593Smuzhiyun goto exit;
5112*4882a593Smuzhiyun }
5113*4882a593Smuzhiyun
5114*4882a593Smuzhiyun scan_type = WL_SCANFLAGS_PASSIVE | WL_SCANFLAGS_LISTEN;
5115*4882a593Smuzhiyun
5116*4882a593Smuzhiyun wl_init_scan_params(cfg, params, params_size,
5117*4882a593Smuzhiyun scan_type, WL_SCAN_ACTION_START, dwell);
5118*4882a593Smuzhiyun
5119*4882a593Smuzhiyun channel_num = (chanspec_num & WL_SCAN_PARAMS_COUNT_MASK);
5120*4882a593Smuzhiyun if (cfg->scan_params_v2) {
5121*4882a593Smuzhiyun eparams_v2 = (wl_escan_params_v2_t *)params;
5122*4882a593Smuzhiyun scanparams_v2 = (wl_scan_params_v2_t *)&eparams_v2->params;
5123*4882a593Smuzhiyun chanspec_list = scanparams_v2->channel_list;
5124*4882a593Smuzhiyun scanparams_v2->channel_num = channel_num;
5125*4882a593Smuzhiyun } else {
5126*4882a593Smuzhiyun eparams = (wl_escan_params_t *)params;
5127*4882a593Smuzhiyun scanparams = (wl_scan_params_t *)&eparams->params;
5128*4882a593Smuzhiyun chanspec_list = scanparams->channel_list;
5129*4882a593Smuzhiyun scanparams->channel_num = channel_num;
5130*4882a593Smuzhiyun }
5131*4882a593Smuzhiyun
5132*4882a593Smuzhiyun /* Copy the single listen channel */
5133*4882a593Smuzhiyun chanspec = wl_freq_to_chanspec(channel->center_freq);
5134*4882a593Smuzhiyun chanspec_list[0] = chanspec;
5135*4882a593Smuzhiyun
5136*4882a593Smuzhiyun err = wldev_iovar_setbuf_bsscfg(ndev, "escan", params, params_size,
5137*4882a593Smuzhiyun cfg->escan_ioctl_buf, WLC_IOCTL_MEDLEN, bssidx, &cfg->ioctl_buf_sync);
5138*4882a593Smuzhiyun if (unlikely(err)) {
5139*4882a593Smuzhiyun if (err == BCME_EPERM) {
5140*4882a593Smuzhiyun /* Scan Not permitted at this point of time */
5141*4882a593Smuzhiyun WL_DBG((" listen not permitted at this time (%d)\n", err));
5142*4882a593Smuzhiyun } else {
5143*4882a593Smuzhiyun WL_ERR((" listen set error (%d)\n", err));
5144*4882a593Smuzhiyun }
5145*4882a593Smuzhiyun goto exit;
5146*4882a593Smuzhiyun } else {
5147*4882a593Smuzhiyun unsigned long listen_timeout = dwell + WL_LISTEN_TIMEOUT;
5148*4882a593Smuzhiyun WL_DBG(("listen started. chanspec:%x\n", chanspec));
5149*4882a593Smuzhiyun cfg->loc.in_progress = true;
5150*4882a593Smuzhiyun cfg->loc.wdev = wdev;
5151*4882a593Smuzhiyun
5152*4882a593Smuzhiyun if (schedule_delayed_work(&cfg->loc.work,
5153*4882a593Smuzhiyun msecs_to_jiffies(listen_timeout))) {
5154*4882a593Smuzhiyun
5155*4882a593Smuzhiyun #if defined(BCMDONGLEHOST) && defined(OEM_ANDROID)
5156*4882a593Smuzhiyun DHD_PM_WAKE_LOCK_TIMEOUT(cfg->pub, listen_timeout);
5157*4882a593Smuzhiyun #endif /* BCMDONGLEHOST && OEM_ANDROID */
5158*4882a593Smuzhiyun
5159*4882a593Smuzhiyun } else {
5160*4882a593Smuzhiyun WL_ERR(("Can't schedule listen work handler\n"));
5161*4882a593Smuzhiyun }
5162*4882a593Smuzhiyun }
5163*4882a593Smuzhiyun
5164*4882a593Smuzhiyun exit:
5165*4882a593Smuzhiyun if (params) {
5166*4882a593Smuzhiyun MFREE(cfg->osh, params, params_size);
5167*4882a593Smuzhiyun }
5168*4882a593Smuzhiyun mutex_unlock(&cfg->scan_sync);
5169*4882a593Smuzhiyun return err;
5170*4882a593Smuzhiyun }
5171*4882a593Smuzhiyun
5172*4882a593Smuzhiyun #define LONG_LISTEN_TIME 2000
5173*4882a593Smuzhiyun #ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST
5174*4882a593Smuzhiyun static void
wl_priortize_scan_over_listen(struct bcm_cfg80211 * cfg,struct net_device * ndev,unsigned int duration)5175*4882a593Smuzhiyun wl_priortize_scan_over_listen(struct bcm_cfg80211 *cfg,
5176*4882a593Smuzhiyun struct net_device *ndev, unsigned int duration)
5177*4882a593Smuzhiyun {
5178*4882a593Smuzhiyun WL_DBG(("scan is running. go to fake listen state\n"));
5179*4882a593Smuzhiyun wl_set_drv_status(cfg, FAKE_REMAINING_ON_CHANNEL, ndev);
5180*4882a593Smuzhiyun
5181*4882a593Smuzhiyun WL_DBG(("cancel current listen timer \n"));
5182*4882a593Smuzhiyun del_timer_sync(&cfg->p2p->listen_timer);
5183*4882a593Smuzhiyun
5184*4882a593Smuzhiyun wl_clr_p2p_status(cfg, LISTEN_EXPIRED);
5185*4882a593Smuzhiyun
5186*4882a593Smuzhiyun INIT_TIMER(&cfg->p2p->listen_timer,
5187*4882a593Smuzhiyun wl_cfgp2p_listen_expired, duration, 0);
5188*4882a593Smuzhiyun }
5189*4882a593Smuzhiyun #endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */
5190*4882a593Smuzhiyun
5191*4882a593Smuzhiyun /* Few vendors use hard coded static ndev p2p0 for p2p disc */
5192*4882a593Smuzhiyun #define IS_P2P_DISC_NDEV(wdev) \
5193*4882a593Smuzhiyun (wdev->netdev ? (strncmp(wdev->netdev->name, "p2p0", strlen("p2p0")) == 0) : false)
5194*4882a593Smuzhiyun
5195*4882a593Smuzhiyun s32
wl_cfgscan_remain_on_channel(struct wiphy * wiphy,bcm_struct_cfgdev * cfgdev,struct ieee80211_channel * channel,enum nl80211_channel_type channel_type,unsigned int duration,u64 * cookie)5196*4882a593Smuzhiyun wl_cfgscan_remain_on_channel(struct wiphy *wiphy, bcm_struct_cfgdev *cfgdev,
5197*4882a593Smuzhiyun struct ieee80211_channel *channel,
5198*4882a593Smuzhiyun #if !defined(WL_CFG80211_P2P_DEV_IF)
5199*4882a593Smuzhiyun enum nl80211_channel_type channel_type,
5200*4882a593Smuzhiyun #endif /* WL_CFG80211_P2P_DEV_IF */
5201*4882a593Smuzhiyun unsigned int duration, u64 *cookie)
5202*4882a593Smuzhiyun {
5203*4882a593Smuzhiyun s32 target_channel;
5204*4882a593Smuzhiyun u32 id;
5205*4882a593Smuzhiyun s32 err = BCME_OK;
5206*4882a593Smuzhiyun struct net_device *ndev = NULL;
5207*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
5208*4882a593Smuzhiyun struct wireless_dev *wdev;
5209*4882a593Smuzhiyun
5210*4882a593Smuzhiyun RETURN_EIO_IF_NOT_UP(cfg);
5211*4882a593Smuzhiyun
5212*4882a593Smuzhiyun mutex_lock(&cfg->usr_sync);
5213*4882a593Smuzhiyun ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);
5214*4882a593Smuzhiyun #if defined(WL_CFG80211_P2P_DEV_IF)
5215*4882a593Smuzhiyun wdev = cfgdev;
5216*4882a593Smuzhiyun #else
5217*4882a593Smuzhiyun wdev = ndev_to_wdev(ndev);
5218*4882a593Smuzhiyun #endif
5219*4882a593Smuzhiyun if (!wdev) {
5220*4882a593Smuzhiyun WL_ERR(("wdev null\n"));
5221*4882a593Smuzhiyun err = -EINVAL;
5222*4882a593Smuzhiyun goto exit;
5223*4882a593Smuzhiyun }
5224*4882a593Smuzhiyun
5225*4882a593Smuzhiyun target_channel = ieee80211_frequency_to_channel(channel->center_freq);
5226*4882a593Smuzhiyun
5227*4882a593Smuzhiyun WL_DBG(("Enter, channel: %d, duration ms (%d) scan_state:%d\n",
5228*4882a593Smuzhiyun target_channel, duration,
5229*4882a593Smuzhiyun (wl_get_drv_status(cfg, SCANNING, ndev)) ? TRUE : FALSE));
5230*4882a593Smuzhiyun
5231*4882a593Smuzhiyun #ifdef WL_BCNRECV
5232*4882a593Smuzhiyun /* check fakeapscan in progress then abort */
5233*4882a593Smuzhiyun wl_android_bcnrecv_stop(ndev, WL_BCNRECV_LISTENBUSY);
5234*4882a593Smuzhiyun #endif /* WL_BCNRECV */
5235*4882a593Smuzhiyun
5236*4882a593Smuzhiyun #if defined(WL_CFG80211_P2P_DEV_IF)
5237*4882a593Smuzhiyun if ((wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) || IS_P2P_DISC_NDEV(wdev))
5238*4882a593Smuzhiyun #else
5239*4882a593Smuzhiyun if (cfg->p2p)
5240*4882a593Smuzhiyun #endif
5241*4882a593Smuzhiyun {
5242*4882a593Smuzhiyun /* p2p discovery */
5243*4882a593Smuzhiyun if (!cfg->p2p) {
5244*4882a593Smuzhiyun WL_ERR(("cfg->p2p is not initialized\n"));
5245*4882a593Smuzhiyun err = BCME_ERROR;
5246*4882a593Smuzhiyun goto exit;
5247*4882a593Smuzhiyun }
5248*4882a593Smuzhiyun
5249*4882a593Smuzhiyun #ifdef P2P_LISTEN_OFFLOADING
5250*4882a593Smuzhiyun if (wl_get_p2p_status(cfg, DISC_IN_PROGRESS)) {
5251*4882a593Smuzhiyun WL_ERR(("P2P_FIND: Discovery offload is in progress\n"));
5252*4882a593Smuzhiyun err = -EAGAIN;
5253*4882a593Smuzhiyun goto exit;
5254*4882a593Smuzhiyun }
5255*4882a593Smuzhiyun #endif /* P2P_LISTEN_OFFLOADING */
5256*4882a593Smuzhiyun
5257*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, SCANNING)) {
5258*4882a593Smuzhiyun #ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST
5259*4882a593Smuzhiyun if (duration > LONG_LISTEN_TIME) {
5260*4882a593Smuzhiyun wl_cfgscan_cancel_scan(cfg);
5261*4882a593Smuzhiyun } else {
5262*4882a593Smuzhiyun wl_priortize_scan_over_listen(cfg, ndev, duration);
5263*4882a593Smuzhiyun err = BCME_OK;
5264*4882a593Smuzhiyun goto exit;
5265*4882a593Smuzhiyun }
5266*4882a593Smuzhiyun #else
5267*4882a593Smuzhiyun wl_cfgscan_cancel_scan(cfg);
5268*4882a593Smuzhiyun #endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */
5269*4882a593Smuzhiyun }
5270*4882a593Smuzhiyun
5271*4882a593Smuzhiyun #ifdef WL_CFG80211_SYNC_GON
5272*4882a593Smuzhiyun if (wl_get_drv_status_all(cfg, WAITING_NEXT_ACT_FRM_LISTEN)) {
5273*4882a593Smuzhiyun /* Do not enter listen mode again if we are in listen mode already
5274*4882a593Smuzhiyun * for next af. Remain on channel completion will be returned by
5275*4882a593Smuzhiyun * af completion.
5276*4882a593Smuzhiyun */
5277*4882a593Smuzhiyun #ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST
5278*4882a593Smuzhiyun wl_set_drv_status(cfg, FAKE_REMAINING_ON_CHANNEL, ndev);
5279*4882a593Smuzhiyun #else
5280*4882a593Smuzhiyun wl_set_drv_status(cfg, REMAINING_ON_CHANNEL, ndev);
5281*4882a593Smuzhiyun #endif
5282*4882a593Smuzhiyun goto exit;
5283*4882a593Smuzhiyun }
5284*4882a593Smuzhiyun #endif /* WL_CFG80211_SYNC_GON */
5285*4882a593Smuzhiyun
5286*4882a593Smuzhiyun if (!cfg->p2p->on) {
5287*4882a593Smuzhiyun /* In case of p2p_listen command, supplicant may send
5288*4882a593Smuzhiyun * remain_on_channel without turning on P2P
5289*4882a593Smuzhiyun */
5290*4882a593Smuzhiyun p2p_on(cfg) = true;
5291*4882a593Smuzhiyun }
5292*4882a593Smuzhiyun
5293*4882a593Smuzhiyun err = wl_cfgp2p_enable_discovery(cfg, ndev, NULL, 0);
5294*4882a593Smuzhiyun if (unlikely(err)) {
5295*4882a593Smuzhiyun goto exit;
5296*4882a593Smuzhiyun }
5297*4882a593Smuzhiyun err = wl_cfgp2p_discover_listen(cfg, target_channel, duration);
5298*4882a593Smuzhiyun if (err == BCME_OK) {
5299*4882a593Smuzhiyun wl_set_drv_status(cfg, REMAINING_ON_CHANNEL, ndev);
5300*4882a593Smuzhiyun } else {
5301*4882a593Smuzhiyun #ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST
5302*4882a593Smuzhiyun if (err == BCME_BUSY) {
5303*4882a593Smuzhiyun /* if failed, firmware may be internal scanning state.
5304*4882a593Smuzhiyun * so other scan request shall not abort it
5305*4882a593Smuzhiyun */
5306*4882a593Smuzhiyun wl_set_drv_status(cfg, FAKE_REMAINING_ON_CHANNEL, ndev);
5307*4882a593Smuzhiyun /* WAR: set err = ok to prevent cookie mismatch in wpa_supplicant
5308*4882a593Smuzhiyun * and expire timer will send a completion to the upper layer
5309*4882a593Smuzhiyun */
5310*4882a593Smuzhiyun err = BCME_OK;
5311*4882a593Smuzhiyun }
5312*4882a593Smuzhiyun #endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */
5313*4882a593Smuzhiyun }
5314*4882a593Smuzhiyun } else if (wdev->iftype == NL80211_IFTYPE_STATION ||
5315*4882a593Smuzhiyun wdev->iftype == NL80211_IFTYPE_AP) {
5316*4882a593Smuzhiyun WL_DBG(("LISTEN ON CHANNEL\n"));
5317*4882a593Smuzhiyun err = wl_cfgscan_listen_on_channel(cfg, wdev, channel, duration);
5318*4882a593Smuzhiyun }
5319*4882a593Smuzhiyun
5320*4882a593Smuzhiyun exit:
5321*4882a593Smuzhiyun if (err == BCME_OK) {
5322*4882a593Smuzhiyun WL_DBG(("Success\n"));
5323*4882a593Smuzhiyun (void)memcpy_s(&cfg->remain_on_chan, sizeof(struct ieee80211_channel),
5324*4882a593Smuzhiyun channel, sizeof(struct ieee80211_channel));
5325*4882a593Smuzhiyun #if defined(WL_ENABLE_P2P_IF)
5326*4882a593Smuzhiyun cfg->remain_on_chan_type = channel_type;
5327*4882a593Smuzhiyun #endif /* WL_ENABLE_P2P_IF */
5328*4882a593Smuzhiyun id = ++cfg->last_roc_id;
5329*4882a593Smuzhiyun if (id == 0) {
5330*4882a593Smuzhiyun id = ++cfg->last_roc_id;
5331*4882a593Smuzhiyun }
5332*4882a593Smuzhiyun *cookie = id;
5333*4882a593Smuzhiyun
5334*4882a593Smuzhiyun /* Notify userspace that listen has started */
5335*4882a593Smuzhiyun CFG80211_READY_ON_CHANNEL(cfgdev, *cookie, channel, channel_type, duration, flags);
5336*4882a593Smuzhiyun WL_INFORM_MEM(("listen started on channel:%d duration (ms):%d cookie:%llu\n",
5337*4882a593Smuzhiyun target_channel, duration, *cookie));
5338*4882a593Smuzhiyun } else {
5339*4882a593Smuzhiyun WL_ERR(("Fail to Set (err=%d cookie:%llu)\n", err, *cookie));
5340*4882a593Smuzhiyun wl_flush_fw_log_buffer(ndev, FW_LOGSET_MASK_ALL);
5341*4882a593Smuzhiyun }
5342*4882a593Smuzhiyun mutex_unlock(&cfg->usr_sync);
5343*4882a593Smuzhiyun return err;
5344*4882a593Smuzhiyun }
5345*4882a593Smuzhiyun
5346*4882a593Smuzhiyun s32
wl_cfgscan_cancel_remain_on_channel(struct wiphy * wiphy,bcm_struct_cfgdev * cfgdev,u64 cookie)5347*4882a593Smuzhiyun wl_cfgscan_cancel_remain_on_channel(struct wiphy *wiphy,
5348*4882a593Smuzhiyun bcm_struct_cfgdev *cfgdev, u64 cookie)
5349*4882a593Smuzhiyun {
5350*4882a593Smuzhiyun s32 err = 0;
5351*4882a593Smuzhiyun struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
5352*4882a593Smuzhiyun #ifdef P2PLISTEN_AP_SAMECHN
5353*4882a593Smuzhiyun struct net_device *dev;
5354*4882a593Smuzhiyun #endif /* P2PLISTEN_AP_SAMECHN */
5355*4882a593Smuzhiyun
5356*4882a593Smuzhiyun RETURN_EIO_IF_NOT_UP(cfg);
5357*4882a593Smuzhiyun
5358*4882a593Smuzhiyun #ifdef DHD_IFDEBUG
5359*4882a593Smuzhiyun PRINT_WDEV_INFO(cfgdev);
5360*4882a593Smuzhiyun #endif /* DHD_IFDEBUG */
5361*4882a593Smuzhiyun
5362*4882a593Smuzhiyun mutex_lock(&cfg->usr_sync);
5363*4882a593Smuzhiyun #if defined(WL_CFG80211_P2P_DEV_IF)
5364*4882a593Smuzhiyun WL_DBG(("cancel listen for iftype:%d\n", cfgdev->iftype));
5365*4882a593Smuzhiyun if ((cfgdev->iftype != NL80211_IFTYPE_P2P_DEVICE) &&
5366*4882a593Smuzhiyun !IS_P2P_DISC_NDEV(cfgdev)) {
5367*4882a593Smuzhiyun /* Handle non-p2p cases here */
5368*4882a593Smuzhiyun err = wl_cfgscan_cancel_listen_on_channel(cfg, false);
5369*4882a593Smuzhiyun goto exit;
5370*4882a593Smuzhiyun }
5371*4882a593Smuzhiyun #else
5372*4882a593Smuzhiyun WL_DBG(("cancel listen for netdev_ifidx: %d \n", cfgdev->ifindex));
5373*4882a593Smuzhiyun #endif /* WL_CFG80211_P2P_DEV_IF */
5374*4882a593Smuzhiyun
5375*4882a593Smuzhiyun #ifdef P2PLISTEN_AP_SAMECHN
5376*4882a593Smuzhiyun if (cfg && cfg->p2p_resp_apchn_status) {
5377*4882a593Smuzhiyun dev = bcmcfg_to_prmry_ndev(cfg);
5378*4882a593Smuzhiyun wl_cfg80211_set_p2p_resp_ap_chn(dev, 0);
5379*4882a593Smuzhiyun cfg->p2p_resp_apchn_status = false;
5380*4882a593Smuzhiyun WL_DBG(("p2p_resp_apchn_status Turn OFF \n"));
5381*4882a593Smuzhiyun }
5382*4882a593Smuzhiyun #endif /* P2PLISTEN_AP_SAMECHN */
5383*4882a593Smuzhiyun
5384*4882a593Smuzhiyun if (cfg->last_roc_id == cookie) {
5385*4882a593Smuzhiyun WL_DBG(("cancel p2p listen. cookie:%llu\n", cookie));
5386*4882a593Smuzhiyun wl_cfgp2p_set_p2p_mode(cfg, WL_P2P_DISC_ST_SCAN, 0, 0,
5387*4882a593Smuzhiyun wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE));
5388*4882a593Smuzhiyun } else {
5389*4882a593Smuzhiyun WL_ERR(("wl_cfg80211_cancel_remain_on_channel: ignore, request cookie(%llu)"
5390*4882a593Smuzhiyun " is not matched. (cur : %llu)\n",
5391*4882a593Smuzhiyun cookie, cfg->last_roc_id));
5392*4882a593Smuzhiyun }
5393*4882a593Smuzhiyun
5394*4882a593Smuzhiyun #if defined(WL_CFG80211_P2P_DEV_IF)
5395*4882a593Smuzhiyun exit:
5396*4882a593Smuzhiyun #endif
5397*4882a593Smuzhiyun mutex_unlock(&cfg->usr_sync);
5398*4882a593Smuzhiyun return err;
5399*4882a593Smuzhiyun }
5400*4882a593Smuzhiyun
5401*4882a593Smuzhiyun #ifdef WL_GET_RCC
5402*4882a593Smuzhiyun int
wl_android_get_roam_scan_chanlist(struct bcm_cfg80211 * cfg)5403*4882a593Smuzhiyun wl_android_get_roam_scan_chanlist(struct bcm_cfg80211 *cfg)
5404*4882a593Smuzhiyun {
5405*4882a593Smuzhiyun s32 err = BCME_OK;
5406*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
5407*4882a593Smuzhiyun struct sk_buff *skb;
5408*4882a593Smuzhiyun gfp_t kflags;
5409*4882a593Smuzhiyun struct net_device *ndev;
5410*4882a593Smuzhiyun struct wiphy *wiphy;
5411*4882a593Smuzhiyun wlc_ssid_t *ssid = NULL;
5412*4882a593Smuzhiyun wl_roam_channel_list_t channel_list;
5413*4882a593Smuzhiyun uint16 channels[MAX_ROAM_CHANNEL] = {0};
5414*4882a593Smuzhiyun int i = 0;
5415*4882a593Smuzhiyun
5416*4882a593Smuzhiyun ndev = bcmcfg_to_prmry_ndev(cfg);
5417*4882a593Smuzhiyun wiphy = bcmcfg_to_wiphy(cfg);
5418*4882a593Smuzhiyun
5419*4882a593Smuzhiyun kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
5420*4882a593Smuzhiyun skb = CFG80211_VENDOR_EVENT_ALLOC(wiphy, ndev_to_wdev(ndev),
5421*4882a593Smuzhiyun BRCM_VENDOR_GET_RCC_EVENT_BUF_LEN, BRCM_VENDOR_EVENT_RCC_INFO, kflags);
5422*4882a593Smuzhiyun
5423*4882a593Smuzhiyun if (!skb) {
5424*4882a593Smuzhiyun WL_ERR(("skb alloc failed"));
5425*4882a593Smuzhiyun return BCME_NOMEM;
5426*4882a593Smuzhiyun }
5427*4882a593Smuzhiyun
5428*4882a593Smuzhiyun /* Get Current SSID */
5429*4882a593Smuzhiyun ssid = (struct wlc_ssid *)wl_read_prof(cfg, ndev, WL_PROF_SSID);
5430*4882a593Smuzhiyun if (!ssid) {
5431*4882a593Smuzhiyun WL_ERR(("No SSID found in the saved profile\n"));
5432*4882a593Smuzhiyun err = BCME_ERROR;
5433*4882a593Smuzhiyun goto fail;
5434*4882a593Smuzhiyun }
5435*4882a593Smuzhiyun
5436*4882a593Smuzhiyun /* Get Current RCC List */
5437*4882a593Smuzhiyun err = wldev_iovar_getbuf(ndev, "roamscan_channels", 0, 0,
5438*4882a593Smuzhiyun (void *)&channel_list, sizeof(channel_list), NULL);
5439*4882a593Smuzhiyun if (err) {
5440*4882a593Smuzhiyun WL_ERR(("Failed to get roamscan channels, err = %d\n", err));
5441*4882a593Smuzhiyun goto fail;
5442*4882a593Smuzhiyun }
5443*4882a593Smuzhiyun if (channel_list.n > MAX_ROAM_CHANNEL) {
5444*4882a593Smuzhiyun WL_ERR(("Invalid roamscan channels count(%d)\n", channel_list.n));
5445*4882a593Smuzhiyun goto fail;
5446*4882a593Smuzhiyun }
5447*4882a593Smuzhiyun
5448*4882a593Smuzhiyun WL_DBG(("SSID %s(%d), RCC(%d)\n", ssid->SSID, ssid->SSID_len, channel_list.n));
5449*4882a593Smuzhiyun for (i = 0; i < channel_list.n; i++) {
5450*4882a593Smuzhiyun channels[i] = CHSPEC_CHANNEL(channel_list.channels[i]);
5451*4882a593Smuzhiyun WL_DBG(("Chanspec[%d] CH:%03d(0x%04x)\n",
5452*4882a593Smuzhiyun i, channels[i], channel_list.channels[i]));
5453*4882a593Smuzhiyun }
5454*4882a593Smuzhiyun
5455*4882a593Smuzhiyun err = nla_put_string(skb, RCC_ATTRIBUTE_SSID, ssid->SSID);
5456*4882a593Smuzhiyun if (unlikely(err)) {
5457*4882a593Smuzhiyun WL_ERR(("nla_put_string RCC_ATTRIBUTE_SSID failed\n"));
5458*4882a593Smuzhiyun goto fail;
5459*4882a593Smuzhiyun }
5460*4882a593Smuzhiyun
5461*4882a593Smuzhiyun err = nla_put_u32(skb, RCC_ATTRIBUTE_SSID_LEN, ssid->SSID_len);
5462*4882a593Smuzhiyun if (unlikely(err)) {
5463*4882a593Smuzhiyun WL_ERR(("nla_put_u32 RCC_ATTRIBUTE_SSID_LEN failed\n"));
5464*4882a593Smuzhiyun goto fail;
5465*4882a593Smuzhiyun }
5466*4882a593Smuzhiyun
5467*4882a593Smuzhiyun err = nla_put_u32(skb, RCC_ATTRIBUTE_NUM_CHANNELS, channel_list.n);
5468*4882a593Smuzhiyun if (unlikely(err)) {
5469*4882a593Smuzhiyun WL_ERR(("nla_put_u32 RCC_ATTRIBUTE_NUM_CHANNELS failed\n"));
5470*4882a593Smuzhiyun goto fail;
5471*4882a593Smuzhiyun }
5472*4882a593Smuzhiyun
5473*4882a593Smuzhiyun err = nla_put(skb, RCC_ATTRIBUTE_CHANNEL_LIST,
5474*4882a593Smuzhiyun sizeof(uint16) * MAX_ROAM_CHANNEL, channels);
5475*4882a593Smuzhiyun if (unlikely(err)) {
5476*4882a593Smuzhiyun WL_ERR(("nla_put RCC_ATTRIBUTE_CHANNEL_LIST failed\n"));
5477*4882a593Smuzhiyun goto fail;
5478*4882a593Smuzhiyun }
5479*4882a593Smuzhiyun
5480*4882a593Smuzhiyun cfg80211_vendor_event(skb, kflags);
5481*4882a593Smuzhiyun
5482*4882a593Smuzhiyun return err;
5483*4882a593Smuzhiyun
5484*4882a593Smuzhiyun fail:
5485*4882a593Smuzhiyun if (skb) {
5486*4882a593Smuzhiyun nlmsg_free(skb);
5487*4882a593Smuzhiyun }
5488*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
5489*4882a593Smuzhiyun return err;
5490*4882a593Smuzhiyun }
5491*4882a593Smuzhiyun #endif /* WL_GET_RCC */
5492*4882a593Smuzhiyun
5493*4882a593Smuzhiyun /*
5494*4882a593Smuzhiyun * This function prepares assoc channel/s
5495*4882a593Smuzhiyun */
5496*4882a593Smuzhiyun s32
wl_get_assoc_channels(struct bcm_cfg80211 * cfg,struct net_device * dev,wlcfg_assoc_info_t * info)5497*4882a593Smuzhiyun wl_get_assoc_channels(struct bcm_cfg80211 *cfg,
5498*4882a593Smuzhiyun struct net_device *dev, wlcfg_assoc_info_t *info)
5499*4882a593Smuzhiyun {
5500*4882a593Smuzhiyun #ifdef ESCAN_CHANNEL_CACHE
5501*4882a593Smuzhiyun s32 err;
5502*4882a593Smuzhiyun u32 max_channels = MAX_ROAM_CHANNEL;
5503*4882a593Smuzhiyun u16 rcc_chan_cnt = 0;
5504*4882a593Smuzhiyun
5505*4882a593Smuzhiyun /*
5506*4882a593Smuzhiyun * If bcast join 'OR' no channel information is provided by user space,
5507*4882a593Smuzhiyun * then use channels from ESCAN_CHANNEL_CACHE. For other cases where target
5508*4882a593Smuzhiyun * channel is available, update RCC via iovar.
5509*4882a593Smuzhiyun *
5510*4882a593Smuzhiyun * For a given SSID there might multiple APs on different channels and FW
5511*4882a593Smuzhiyun * would scan all those channels before deciding up on the AP.
5512*4882a593Smuzhiyun */
5513*4882a593Smuzhiyun if (cfg->rcc_enabled) {
5514*4882a593Smuzhiyun wlc_ssid_t ssid;
5515*4882a593Smuzhiyun int band;
5516*4882a593Smuzhiyun chanspec_t chanspecs[MAX_ROAM_CHANNEL] = {0};
5517*4882a593Smuzhiyun chanspec_t target_chspec;
5518*4882a593Smuzhiyun
5519*4882a593Smuzhiyun err = wldev_get_band(dev, &band);
5520*4882a593Smuzhiyun if (!err) {
5521*4882a593Smuzhiyun set_roam_band(band);
5522*4882a593Smuzhiyun }
5523*4882a593Smuzhiyun
5524*4882a593Smuzhiyun if (memcpy_s(ssid.SSID, sizeof(ssid.SSID), info->ssid, info->ssid_len) != BCME_OK) {
5525*4882a593Smuzhiyun WL_ERR(("ssid copy failed\n"));
5526*4882a593Smuzhiyun return -EINVAL;
5527*4882a593Smuzhiyun }
5528*4882a593Smuzhiyun ssid.SSID_len = (uint32)info->ssid_len;
5529*4882a593Smuzhiyun
5530*4882a593Smuzhiyun if (info->targeted_join && info->chanspecs[0]) {
5531*4882a593Smuzhiyun target_chspec = info->chanspecs[0];
5532*4882a593Smuzhiyun } else {
5533*4882a593Smuzhiyun target_chspec = INVCHANSPEC;
5534*4882a593Smuzhiyun }
5535*4882a593Smuzhiyun rcc_chan_cnt = get_roam_channel_list(cfg, target_chspec, chanspecs,
5536*4882a593Smuzhiyun max_channels, &ssid, ioctl_version);
5537*4882a593Smuzhiyun if ((!info->targeted_join) || (info->bssid_hint) ||
5538*4882a593Smuzhiyun (info->chan_cnt == 0)) {
5539*4882a593Smuzhiyun #if !defined(DISABLE_FW_NW_SEL_FOR_6G) && defined(WL_6G_BAND)
5540*4882a593Smuzhiyun int i;
5541*4882a593Smuzhiyun /* If 6G AP is present, override bssid_hint with our fw nw
5542*4882a593Smuzhiyun * selection. Supplicant bssid_hint logic doesn't have support for
5543*4882a593Smuzhiyun * 6G, HE, OCE load IE support
5544*4882a593Smuzhiyun */
5545*4882a593Smuzhiyun for (i = 0; i < rcc_chan_cnt; i++) {
5546*4882a593Smuzhiyun if (CHSPEC_IS6G(chanspecs[i])) {
5547*4882a593Smuzhiyun WL_INFORM_MEM(("6G channel in rcc. use fw nw sel\n"));
5548*4882a593Smuzhiyun /* skip bssid hint inclusion and provide bcast bssid */
5549*4882a593Smuzhiyun info->bssid_hint = false;
5550*4882a593Smuzhiyun (void)memcpy_s(&info->bssid,
5551*4882a593Smuzhiyun ETH_ALEN, ðer_bcast, ETH_ALEN);
5552*4882a593Smuzhiyun break;
5553*4882a593Smuzhiyun }
5554*4882a593Smuzhiyun }
5555*4882a593Smuzhiyun #endif /* !DISABLE_FW_NW_SEL_FOR_6G && WL_6G_BAND */
5556*4882a593Smuzhiyun /* Use RCC channels as part of join params */
5557*4882a593Smuzhiyun info->chan_cnt = rcc_chan_cnt;
5558*4882a593Smuzhiyun if (memcpy_s(info->chanspecs, sizeof(info->chanspecs), chanspecs,
5559*4882a593Smuzhiyun (sizeof(chanspec_t) * rcc_chan_cnt)) != BCME_OK) {
5560*4882a593Smuzhiyun WL_ERR(("chanspec copy failed!\n"));
5561*4882a593Smuzhiyun return -EINVAL;
5562*4882a593Smuzhiyun }
5563*4882a593Smuzhiyun }
5564*4882a593Smuzhiyun }
5565*4882a593Smuzhiyun #endif /* ESCAN_CHANNEL_CACHE */
5566*4882a593Smuzhiyun
5567*4882a593Smuzhiyun WL_SCAN(("channel cnt:%d\n", info->chan_cnt));
5568*4882a593Smuzhiyun return BCME_OK;
5569*4882a593Smuzhiyun }
5570*4882a593Smuzhiyun
5571*4882a593Smuzhiyun #ifdef DHD_GET_VALID_CHANNELS
5572*4882a593Smuzhiyun bool
wl_cfgscan_is_dfs_set(wifi_band band)5573*4882a593Smuzhiyun wl_cfgscan_is_dfs_set(wifi_band band)
5574*4882a593Smuzhiyun {
5575*4882a593Smuzhiyun switch (band) {
5576*4882a593Smuzhiyun case WIFI_BAND_A_DFS:
5577*4882a593Smuzhiyun case WIFI_BAND_A_WITH_DFS:
5578*4882a593Smuzhiyun case WIFI_BAND_ABG_WITH_DFS:
5579*4882a593Smuzhiyun case WIFI_BAND_24GHZ_5GHZ_WITH_DFS_6GHZ:
5580*4882a593Smuzhiyun return true;
5581*4882a593Smuzhiyun default:
5582*4882a593Smuzhiyun return false;
5583*4882a593Smuzhiyun }
5584*4882a593Smuzhiyun return false;
5585*4882a593Smuzhiyun }
5586*4882a593Smuzhiyun
5587*4882a593Smuzhiyun s32
wl_cfgscan_get_band_freq_list(struct bcm_cfg80211 * cfg,int band,uint32 * list,uint32 * num_channels)5588*4882a593Smuzhiyun wl_cfgscan_get_band_freq_list(struct bcm_cfg80211 *cfg, int band,
5589*4882a593Smuzhiyun uint32 *list, uint32 *num_channels)
5590*4882a593Smuzhiyun {
5591*4882a593Smuzhiyun s32 err = BCME_OK;
5592*4882a593Smuzhiyun uint32 i, freq, list_count, count = 0;
5593*4882a593Smuzhiyun struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
5594*4882a593Smuzhiyun uint32 chspec, chaninfo;
5595*4882a593Smuzhiyun bool dfs_set = false;
5596*4882a593Smuzhiyun
5597*4882a593Smuzhiyun dfs_set = wl_cfgscan_is_dfs_set(band);
5598*4882a593Smuzhiyun err = wldev_iovar_getbuf_bsscfg(dev, "chan_info_list", NULL,
5599*4882a593Smuzhiyun 0, list, CHANINFO_LIST_BUF_SIZE, 0, &cfg->ioctl_buf_sync);
5600*4882a593Smuzhiyun if (err == BCME_UNSUPPORTED) {
5601*4882a593Smuzhiyun WL_INFORM(("get chan_info_list, UNSUPPORTED\n"));
5602*4882a593Smuzhiyun return err;
5603*4882a593Smuzhiyun } else if (err != BCME_OK) {
5604*4882a593Smuzhiyun WL_ERR(("get chan_info_list err(%d)\n", err));
5605*4882a593Smuzhiyun return err;
5606*4882a593Smuzhiyun }
5607*4882a593Smuzhiyun
5608*4882a593Smuzhiyun list_count = ((wl_chanspec_list_v1_t *)list)->count;
5609*4882a593Smuzhiyun for (i = 0; i < list_count; i++) {
5610*4882a593Smuzhiyun chspec = dtoh32(((wl_chanspec_list_v1_t *)list)->chspecs[i].chanspec);
5611*4882a593Smuzhiyun if (!CHSPEC_IS20(chspec)) {
5612*4882a593Smuzhiyun continue;
5613*4882a593Smuzhiyun }
5614*4882a593Smuzhiyun chaninfo = dtoh32(((wl_chanspec_list_v1_t *)list)->chspecs[i].chaninfo);
5615*4882a593Smuzhiyun freq = wl_channel_to_frequency(wf_chspec_ctlchan(chspec),
5616*4882a593Smuzhiyun CHSPEC_BAND(chspec));
5617*4882a593Smuzhiyun if (((band & WIFI_BAND_BG) && CHSPEC_IS2G(chspec)) ||
5618*4882a593Smuzhiyun ((band & WIFI_BAND_6GHZ) && CHSPEC_IS6G(chspec))) {
5619*4882a593Smuzhiyun /* add 2g/6g channels */
5620*4882a593Smuzhiyun list[count] = freq;
5621*4882a593Smuzhiyun count++;
5622*4882a593Smuzhiyun }
5623*4882a593Smuzhiyun /* handle 5g separately */
5624*4882a593Smuzhiyun if (CHSPEC_IS5G(chspec)) {
5625*4882a593Smuzhiyun if (!((band == WIFI_BAND_A_DFS) && IS_DFS(chaninfo)) &&
5626*4882a593Smuzhiyun !(band & WIFI_BAND_A)) {
5627*4882a593Smuzhiyun /* Not DFS only case nor 5G case */
5628*4882a593Smuzhiyun continue;
5629*4882a593Smuzhiyun }
5630*4882a593Smuzhiyun
5631*4882a593Smuzhiyun if ((band & WIFI_BAND_A) && !dfs_set && IS_DFS(chaninfo)) {
5632*4882a593Smuzhiyun continue;
5633*4882a593Smuzhiyun }
5634*4882a593Smuzhiyun
5635*4882a593Smuzhiyun list[count] = freq;
5636*4882a593Smuzhiyun count++;
5637*4882a593Smuzhiyun }
5638*4882a593Smuzhiyun }
5639*4882a593Smuzhiyun *num_channels = count;
5640*4882a593Smuzhiyun return err;
5641*4882a593Smuzhiyun }
5642*4882a593Smuzhiyun #endif /* DHD_GET_VALID_CHANNELS */
5643