xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/wl_bam.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Bad AP Manager for ADPS
3  *
4  * Copyright (C) 2020, Broadcom.
5  *
6  *      Unless you and Broadcom execute a separate written software license
7  * agreement governing use of this software, this software is licensed to you
8  * under the terms of the GNU General Public License version 2 (the "GPL"),
9  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10  * following added to such license:
11  *
12  *      As a special exception, the copyright holders of this software give you
13  * permission to link this software with independent modules, and to copy and
14  * distribute the resulting executable under terms of your choice, provided that
15  * you also meet, for each linked independent module, the terms and conditions of
16  * the license of that module.  An independent module is a module which is not
17  * derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *
21  * <<Broadcom-WL-IPTag/Dual:>>
22  */
23 #include <linuxver.h>
24 #include <bcmiov.h>
25 #include <linux/list_sort.h>
26 #include <wl_cfg80211.h>
27 #include <wlioctl.h>
28 #include <wldev_common.h>
29 #include <wl_bam.h>
30 
31 static int
wl_bad_ap_mngr_add_entry(wl_bad_ap_mngr_t * bad_ap_mngr,wl_bad_ap_info_t * bad_ap_info)32 wl_bad_ap_mngr_add_entry(wl_bad_ap_mngr_t *bad_ap_mngr, wl_bad_ap_info_t *bad_ap_info)
33 {
34 	unsigned long flags;
35 	wl_bad_ap_info_entry_t *entry;
36 
37 	entry = MALLOCZ(bad_ap_mngr->osh, sizeof(*entry));
38 	if (entry == NULL) {
39 		WL_ERR(("%s: allocation for list failed\n", __FUNCTION__));
40 		return BCME_NOMEM;
41 	}
42 
43 	memcpy(&entry->bad_ap, bad_ap_info, sizeof(entry->bad_ap));
44 	INIT_LIST_HEAD(&entry->list);
45 	spin_lock_irqsave(&bad_ap_mngr->lock, flags);
46 	list_add_tail(&entry->list, &bad_ap_mngr->list);
47 	spin_unlock_irqrestore(&bad_ap_mngr->lock, flags);
48 
49 	bad_ap_mngr->num++;
50 
51 	return BCME_OK;
52 }
53 
54 #if !defined(DHD_ADPS_BAM_EXPORT)
55 #define WL_BAD_AP_INFO_FILE_PATH	PLATFORM_PATH".bad_ap_list.info"
56 #define WL_BAD_AP_MAX_BUF_SIZE		1024u
57 
58 /* Bad AP information data format
59  *
60  * Status and Reason: come from event
61  * Connection count: Increase connecting Bad AP
62  *
63  * BSSID,year-month-day hour:min:sec,Status,Reason,Connection count
64  * ex) XX:XX:XX:XX:XX:XX,1970-01-01 00:00:00,1,2,1
65  *
66  */
67 #define WL_BAD_AP_INFO_FMT \
68 	"%02x:%02x:%02x:%02x:%02x:%02x,%04ld-%02d-%02d %02d:%02d:%02d,%u,%u,%u\n"
69 #define WL_BAD_AP_INFO_FMT_ITEM_CNT	15u
70 
71 static inline void
wl_bad_ap_mngr_tm2ts(struct timespec * ts,const struct tm tm)72 wl_bad_ap_mngr_tm2ts(struct timespec *ts, const struct tm tm)
73 {
74 	ts->tv_sec = mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
75 	ts->tv_nsec = 0;
76 }
77 
78 /* Ignore compiler warnings due to -Werror=cast-qual */
79 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
80 #pragma GCC diagnostic push
81 #pragma GCC diagnostic ignored "-Wcast-qual"
82 #endif
83 static int
wl_bad_ap_mngr_timecmp(void * priv,struct list_head * a,struct list_head * b)84 wl_bad_ap_mngr_timecmp(void *priv, struct list_head *a, struct list_head *b)
85 {
86 	int ret;
87 
88 	struct timespec ts1;
89 	struct timespec ts2;
90 
91 	wl_bad_ap_info_entry_t *e1 = CONTAINEROF(a, wl_bad_ap_info_entry_t, list);
92 	wl_bad_ap_info_entry_t *e2 = CONTAINEROF(b, wl_bad_ap_info_entry_t, list);
93 
94 	wl_bad_ap_mngr_tm2ts(&ts1, e1->bad_ap.tm);
95 	wl_bad_ap_mngr_tm2ts(&ts2, e2->bad_ap.tm);
96 
97 	ret = timespec_compare((const struct timespec *)&ts1, (const struct timespec *)&ts2);
98 
99 	return ret;
100 }
101 
102 static void
wl_bad_ap_mngr_update(struct bcm_cfg80211 * cfg,wl_bad_ap_info_t * bad_ap_info)103 wl_bad_ap_mngr_update(struct bcm_cfg80211 *cfg, wl_bad_ap_info_t *bad_ap_info)
104 {
105 	wl_bad_ap_info_entry_t *entry;
106 	unsigned long flags;
107 
108 	if (list_empty(&cfg->bad_ap_mngr.list)) {
109 		return;
110 	}
111 
112 	WL_CFG_BAM_LOCK(&cfg->bad_ap_mngr.lock, flags);
113 	/* sort by timestamp */
114 	list_sort(NULL, &cfg->bad_ap_mngr.list, wl_bad_ap_mngr_timecmp);
115 
116 	/* update entry with the latest bad ap information */
117 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
118 #pragma GCC diagnostic push
119 #pragma GCC diagnostic ignored "-Wcast-qual"
120 #endif
121 	entry = list_first_entry(&cfg->bad_ap_mngr.list, wl_bad_ap_info_entry_t, list);
122 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
123 #pragma GCC diagnostic pop
124 #endif
125 	if (entry != NULL) {
126 		memcpy(&entry->bad_ap, bad_ap_info, sizeof(entry->bad_ap));
127 	}
128 	WL_CFG_BAM_UNLOCK(&cfg->bad_ap_mngr.lock, flags);
129 }
130 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
131 #pragma GCC diagnostic pop
132 #endif
133 
134 static inline int
wl_bad_ap_mngr_fread_bad_ap_info(char * buf,int buf_len,wl_bad_ap_info_t * bad_ap)135 wl_bad_ap_mngr_fread_bad_ap_info(char *buf, int buf_len, wl_bad_ap_info_t *bad_ap)
136 {
137 	return snprintf(buf, buf_len, WL_BAD_AP_INFO_FMT,
138 			bad_ap->bssid.octet[0], bad_ap->bssid.octet[1],
139 			bad_ap->bssid.octet[2], bad_ap->bssid.octet[3],
140 			bad_ap->bssid.octet[4], bad_ap->bssid.octet[5],
141 			bad_ap->tm.tm_year + 1900, bad_ap->tm.tm_mon + 1, bad_ap->tm.tm_mday,
142 			bad_ap->tm.tm_hour, bad_ap->tm.tm_min, bad_ap->tm.tm_sec,
143 			bad_ap->status, bad_ap->reason, bad_ap->connect_count);
144 }
145 
146 static int
wl_bad_ap_mngr_fparse(struct bcm_cfg80211 * cfg,struct file * fp)147 wl_bad_ap_mngr_fparse(struct bcm_cfg80211 *cfg, struct file *fp)
148 {
149 	int len;
150 	int pos = 0;
151 	char tmp[128];
152 	int ret = BCME_ERROR;
153 
154 	wl_bad_ap_info_t bad_ap;
155 	char *buf = NULL;
156 
157 	buf = MALLOCZ(cfg->osh, WL_BAD_AP_MAX_BUF_SIZE);
158 	if (buf == NULL) {
159 		WL_ERR(("%s: allocation for buf failed\n", __FUNCTION__));
160 		return BCME_NOMEM;
161 	}
162 
163 	ret = dhd_vfs_read(fp, buf, WL_BAD_AP_MAX_BUF_SIZE, &fp->f_pos);
164 	if (ret  < 0) {
165 		WL_ERR(("%s: file read failed (%d)\n", __FUNCTION__, ret));
166 		goto fail;
167 	}
168 
169 	len = ret;
170 	do {
171 		ret = sscanf(&buf[pos], WL_BAD_AP_INFO_FMT,
172 				(uint32 *)&bad_ap.bssid.octet[0], (uint32 *)&bad_ap.bssid.octet[1],
173 				(uint32 *)&bad_ap.bssid.octet[2], (uint32 *)&bad_ap.bssid.octet[3],
174 				(uint32 *)&bad_ap.bssid.octet[4], (uint32 *)&bad_ap.bssid.octet[5],
175 				(long int *)&bad_ap.tm.tm_year, (uint32 *)&bad_ap.tm.tm_mon,
176 				(uint32 *)&bad_ap.tm.tm_mday, (uint32 *)&bad_ap.tm.tm_hour,
177 				(uint32 *)&bad_ap.tm.tm_min, (uint32 *)&bad_ap.tm.tm_sec,
178 				(uint32 *)&bad_ap.status, (uint32 *)&bad_ap.reason,
179 				(uint32 *)&bad_ap.connect_count);
180 		if (ret != WL_BAD_AP_INFO_FMT_ITEM_CNT) {
181 			WL_ERR(("%s: file parse failed(expected: %d actual: %d)\n",
182 				__FUNCTION__, WL_BAD_AP_INFO_FMT_ITEM_CNT, ret));
183 			ret = BCME_ERROR;
184 			goto fail;
185 		}
186 
187 		/* convert struct tm format */
188 		bad_ap.tm.tm_year -= 1900;
189 		bad_ap.tm.tm_mon -= 1;
190 
191 		ret = wl_bad_ap_mngr_add(&cfg->bad_ap_mngr, &bad_ap);
192 		if (ret < 0) {
193 			WL_ERR(("%s: bad ap add failed\n", __FUNCTION__));
194 			goto fail;
195 		}
196 
197 		ret = wl_bad_ap_mngr_fread_bad_ap_info(tmp, sizeof(tmp), &bad_ap);
198 		if (ret < 0) {
199 			WL_ERR(("%s: wl_bad_ap_mngr_fread_bad_ap_info failed (%d)\n",
200 				__FUNCTION__, ret));
201 			goto fail;
202 		}
203 
204 		if (cfg->bad_ap_mngr.num >= WL_BAD_AP_MAX_ENTRY_NUM) {
205 			break;
206 		}
207 
208 		len -= ret;
209 		pos += ret;
210 	} while (len > 0);
211 
212 	ret = BCME_OK;
213 
214 fail:
215 	if (buf) {
216 		MFREE(cfg->osh, buf, WL_BAD_AP_MAX_BUF_SIZE);
217 	}
218 
219 	return ret;
220 }
221 
222 static int
wl_bad_ap_mngr_fread(struct bcm_cfg80211 * cfg,const char * fname)223 wl_bad_ap_mngr_fread(struct bcm_cfg80211 *cfg, const char *fname)
224 {
225 	int ret = BCME_ERROR;
226 
227 	mm_segment_t fs;
228 	struct file *fp = NULL;
229 
230 	if (fname == NULL) {
231 		WL_ERR(("%s: fname is NULL\n", __FUNCTION__));
232 		return ret;
233 	}
234 	mutex_lock(&cfg->bad_ap_mngr.fs_lock);
235 
236 	fs = get_fs();
237 	set_fs(KERNEL_DS);
238 
239 	fp = dhd_filp_open(fname, O_RDONLY, 0);
240 	if (IS_ERR(fp) || (fp == NULL)) {
241 		fp = NULL;
242 		WL_ERR(("%s: file open failed(%d)\n", __FUNCTION__, ret));
243 		goto fail;
244 	}
245 
246 	if ((ret = wl_bad_ap_mngr_fparse(cfg, fp)) < 0) {
247 		goto fail;
248 	}
249 fail:
250 	if (fp) {
251 		dhd_filp_close(fp, NULL);
252 	}
253 	set_fs(fs);
254 
255 	mutex_unlock(&cfg->bad_ap_mngr.fs_lock);
256 
257 	return ret;
258 }
259 
260 static int
wl_bad_ap_mngr_fwrite(struct bcm_cfg80211 * cfg,const char * fname)261 wl_bad_ap_mngr_fwrite(struct bcm_cfg80211 *cfg, const char *fname)
262 {
263 	int ret = BCME_ERROR;
264 
265 	mm_segment_t fs;
266 	struct file *fp = NULL;
267 
268 	int len = 0;
269 	char tmp[WL_BAD_AP_MAX_BUF_SIZE];
270 	wl_bad_ap_info_t *bad_ap;
271 	wl_bad_ap_info_entry_t *entry;
272 
273 	if (list_empty(&cfg->bad_ap_mngr.list)) {
274 		return BCME_ERROR;
275 	}
276 
277 	if (fname == NULL) {
278 		return BCME_NOTFOUND;
279 	}
280 
281 	mutex_lock(&cfg->bad_ap_mngr.fs_lock);
282 
283 	fs = get_fs();
284 	set_fs(KERNEL_DS);
285 
286 	fp = dhd_filp_open(fname, O_CREAT | O_RDWR | O_TRUNC,  0666);
287 	if (IS_ERR(fp) || (fp == NULL)) {
288 		ret = PTR_ERR(fp);
289 		WL_ERR(("%s: file open failed(%d)\n", __FUNCTION__, ret));
290 		fp = NULL;
291 		goto fail;
292 	}
293 
294 	memset(tmp, 0, sizeof(tmp));
295 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
296 #pragma GCC diagnostic push
297 #pragma GCC diagnostic ignored "-Wcast-qual"
298 #endif
299 	list_for_each_entry(entry, &cfg->bad_ap_mngr.list, list) {
300 		bad_ap = &entry->bad_ap;
301 		ret = wl_bad_ap_mngr_fread_bad_ap_info(&tmp[len], sizeof(tmp) - len, bad_ap);
302 		if (ret < 0) {
303 			WL_ERR(("%s: snprintf failed(%d)\n", __FUNCTION__, ret));
304 			goto fail;
305 		}
306 
307 		len += ret;
308 	}
309 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
310 #pragma GCC diagnostic pop
311 #endif
312 
313 	ret = dhd_vfs_write(fp, tmp, len, &fp->f_pos);
314 	if (ret < 0) {
315 		WL_ERR(("%s: file write failed(%d)\n", __FUNCTION__, ret));
316 		goto fail;
317 	}
318 	/* Sync file from filesystem to physical media */
319 	ret = dhd_vfs_fsync(fp, 0);
320 	if (ret < 0) {
321 		WL_ERR(("%s: sync file failed(%d)\n", __FUNCTION__, ret));
322 		goto fail;
323 	}
324 	ret = BCME_OK;
325 fail:
326 	if (fp) {
327 		dhd_filp_close(fp, NULL);
328 	}
329 	set_fs(fs);
330 	mutex_unlock(&cfg->bad_ap_mngr.fs_lock);
331 
332 	return ret;
333 }
334 #else
335 extern wl_bad_ap_mngr_t *g_bad_ap_mngr;
336 #endif	/* DHD_ADPS_BAM_EXPORT */
337 
338 wl_bad_ap_info_entry_t*
wl_bad_ap_mngr_find(wl_bad_ap_mngr_t * bad_ap_mngr,const struct ether_addr * bssid)339 wl_bad_ap_mngr_find(wl_bad_ap_mngr_t *bad_ap_mngr, const struct ether_addr *bssid)
340 {
341 	wl_bad_ap_info_entry_t *entry;
342 	unsigned long flags;
343 
344 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
345 #pragma GCC diagnostic push
346 #pragma GCC diagnostic ignored "-Wcast-qual"
347 #endif
348 	spin_lock_irqsave(&bad_ap_mngr->lock, flags);
349 	list_for_each_entry(entry, &bad_ap_mngr->list, list) {
350 		if (!memcmp(&entry->bad_ap.bssid.octet, bssid->octet, ETHER_ADDR_LEN)) {
351 			spin_unlock_irqrestore(&bad_ap_mngr->lock, flags);
352 			return entry;
353 		}
354 	}
355 	spin_unlock_irqrestore(&bad_ap_mngr->lock, flags);
356 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
357 #pragma GCC diagnostic pop
358 #endif
359 	return NULL;
360 }
361 
362 int
wl_bad_ap_mngr_add(wl_bad_ap_mngr_t * bad_ap_mngr,wl_bad_ap_info_t * bad_ap_info)363 wl_bad_ap_mngr_add(wl_bad_ap_mngr_t *bad_ap_mngr, wl_bad_ap_info_t *bad_ap_info)
364 {
365 	int ret;
366 	wl_bad_ap_info_entry_t *entry;
367 	unsigned long flags;
368 
369 	BCM_REFERENCE(entry);
370 	BCM_REFERENCE(flags);
371 
372 #if !defined(DHD_ADPS_BAM_EXPORT)
373 	ret = wl_bad_ap_mngr_add_entry(bad_ap_mngr, bad_ap_info);
374 #else
375 	if (bad_ap_mngr->num == WL_BAD_AP_MAX_ENTRY_NUM) {
376 		/* Remove the oldest entry if entry list is full */
377 		spin_lock_irqsave(&bad_ap_mngr->lock, flags);
378 		list_del(bad_ap_mngr->list.next);
379 		bad_ap_mngr->num--;
380 		spin_unlock_irqrestore(&bad_ap_mngr->lock, flags);
381 	}
382 
383 	/* delete duplicated entry to update it at tail to keep the odrer */
384 	entry = wl_bad_ap_mngr_find(bad_ap_mngr, &bad_ap_info->bssid);
385 	if (entry != NULL) {
386 		spin_lock_irqsave(&bad_ap_mngr->lock, flags);
387 		list_del(&entry->list);
388 		bad_ap_mngr->num--;
389 		spin_unlock_irqrestore(&bad_ap_mngr->lock, flags);
390 	}
391 
392 	ret = wl_bad_ap_mngr_add_entry(bad_ap_mngr, bad_ap_info);
393 	if (ret < 0) {
394 		WL_ERR(("%s - fail to add bad ap data(%d)\n", __FUNCTION__, ret));
395 		return ret;
396 	}
397 #endif	/* DHD_ADPS_BAM_EXPORT */
398 	return ret;
399 }
400 
401 void
wl_bad_ap_mngr_deinit(struct bcm_cfg80211 * cfg)402 wl_bad_ap_mngr_deinit(struct bcm_cfg80211 *cfg)
403 {
404 	wl_bad_ap_info_entry_t *entry;
405 	unsigned long flags;
406 
407 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
408 #pragma GCC diagnostic push
409 #pragma GCC diagnostic ignored "-Wcast-qual"
410 #endif
411 	WL_CFG_BAM_LOCK(&cfg->bad_ap_mngr.lock, flags);
412 	while (!list_empty(&cfg->bad_ap_mngr.list)) {
413 		entry = list_entry(cfg->bad_ap_mngr.list.next, wl_bad_ap_info_entry_t, list);
414 		if (entry) {
415 			list_del(&cfg->bad_ap_mngr.list);
416 			MFREE(cfg->osh, entry, sizeof(*entry));
417 		}
418 	}
419 	WL_CFG_BAM_UNLOCK(&cfg->bad_ap_mngr.lock, flags);
420 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
421 #pragma GCC diagnostic pop
422 #endif
423 #if !defined(DHD_ADPS_BAM_EXPORT)
424 	mutex_destroy(&cfg->bad_ap_mngr.fs_lock);
425 #endif	/* !DHD_ADPS_BAM_EXPORT */
426 }
427 
428 void
wl_bad_ap_mngr_init(struct bcm_cfg80211 * cfg)429 wl_bad_ap_mngr_init(struct bcm_cfg80211 *cfg)
430 {
431 	cfg->bad_ap_mngr.osh = cfg->osh;
432 	cfg->bad_ap_mngr.num = 0;
433 
434 	spin_lock_init(&cfg->bad_ap_mngr.lock);
435 	INIT_LIST_HEAD(&cfg->bad_ap_mngr.list);
436 
437 #if !defined(DHD_ADPS_BAM_EXPORT)
438 	mutex_init(&cfg->bad_ap_mngr.fs_lock);
439 #else
440 	g_bad_ap_mngr = &cfg->bad_ap_mngr;
441 #endif	/* !DHD_ADPS_BAM_EXPORT */
442 }
443 
444 static int
wl_event_adps_bad_ap_mngr(struct bcm_cfg80211 * cfg,void * data)445 wl_event_adps_bad_ap_mngr(struct bcm_cfg80211 *cfg, void *data)
446 {
447 	int ret = BCME_OK;
448 
449 	wl_event_adps_t *event_data = (wl_event_adps_t *)data;
450 	wl_event_adps_bad_ap_t *bad_ap_data;
451 
452 	wl_bad_ap_info_entry_t *entry;
453 	wl_bad_ap_info_t temp;
454 #if !defined(DHD_ADPS_BAM_EXPORT)
455 	struct timespec ts;
456 #endif	/* !DHD_ADPS_BAM_EXPORT */
457 
458 	if (event_data->version != WL_EVENT_ADPS_VER_1) {
459 		return BCME_VERSION;
460 	}
461 
462 	if (event_data->length != (OFFSETOF(wl_event_adps_t, data) + sizeof(*bad_ap_data))) {
463 		return BCME_ERROR;
464 	}
465 
466 	BCM_REFERENCE(ret);
467 	BCM_REFERENCE(entry);
468 	bad_ap_data = (wl_event_adps_bad_ap_t *)event_data->data;
469 
470 #if !defined(DHD_ADPS_BAM_EXPORT)
471 	/* Update Bad AP list */
472 	if (list_empty(&cfg->bad_ap_mngr.list)) {
473 		wl_bad_ap_mngr_fread(cfg, WL_BAD_AP_INFO_FILE_PATH);
474 	}
475 
476 	getnstimeofday(&ts);
477 	entry = wl_bad_ap_mngr_find(&cfg->bad_ap_mngr, &bad_ap_data->ea);
478 	if (entry != NULL) {
479 		time_to_tm((ts.tv_sec - (sys_tz.tz_minuteswest * 60)), 0, &entry->bad_ap.tm);
480 		entry->bad_ap.status = bad_ap_data->status;
481 		entry->bad_ap.reason = bad_ap_data->reason;
482 		entry->bad_ap.connect_count++;
483 	}
484 	else {
485 		time_to_tm((ts.tv_sec - (sys_tz.tz_minuteswest * 60)), 0, &temp.tm);
486 		temp.status = bad_ap_data->status;
487 		temp.reason = bad_ap_data->reason;
488 		temp.connect_count = 1;
489 		memcpy(temp.bssid.octet, &bad_ap_data->ea.octet, ETHER_ADDR_LEN);
490 
491 		if (cfg->bad_ap_mngr.num < WL_BAD_AP_MAX_ENTRY_NUM) {
492 			wl_bad_ap_mngr_add(&cfg->bad_ap_mngr, &temp);
493 		}
494 		else {
495 			wl_bad_ap_mngr_update(cfg, &temp);
496 		}
497 	}
498 
499 	wl_bad_ap_mngr_fwrite(cfg, WL_BAD_AP_INFO_FILE_PATH);
500 #else
501 	memcpy(temp.bssid.octet, &bad_ap_data->ea.octet, ETHER_ADDR_LEN);
502 	ret = wl_bad_ap_mngr_add(&cfg->bad_ap_mngr, &temp);
503 #endif	/* !DHD_ADPS_BAM_EXPORT */
504 
505 	return ret;
506 }
507 
508 static int
wl_adps_get_mode(struct net_device * ndev,uint8 band)509 wl_adps_get_mode(struct net_device *ndev, uint8 band)
510 {
511 	int len;
512 	int ret;
513 
514 	uint8 *pdata;
515 	char buf[WLC_IOCTL_SMLEN];
516 
517 	bcm_iov_buf_t iov_buf;
518 	bcm_iov_buf_t *resp;
519 	wl_adps_params_v1_t *data = NULL;
520 
521 	memset(&iov_buf, 0, sizeof(iov_buf));
522 	len = OFFSETOF(bcm_iov_buf_t, data) + sizeof(band);
523 
524 	iov_buf.version = WL_ADPS_IOV_VER;
525 	iov_buf.len = sizeof(band);
526 	iov_buf.id = WL_ADPS_IOV_MODE;
527 	pdata = (uint8 *)iov_buf.data;
528 	*pdata = band;
529 
530 	ret = wldev_iovar_getbuf(ndev, "adps", &iov_buf, len, buf, WLC_IOCTL_SMLEN, NULL);
531 	if (ret < 0) {
532 		return ret;
533 	}
534 	resp = (bcm_iov_buf_t *)buf;
535 	data = (wl_adps_params_v1_t *)resp->data;
536 
537 	return data->mode;
538 }
539 
540 /*
541  * Return value:
542  *  Disabled: 0
543  *  Enabled: bitmap of WLC_BAND_2G or WLC_BAND_5G when ADPS is enabled at each BAND
544  *
545  */
546 int
wl_adps_enabled(struct bcm_cfg80211 * cfg,struct net_device * ndev)547 wl_adps_enabled(struct bcm_cfg80211 *cfg, struct net_device *ndev)
548 {
549 	uint8 i;
550 	int mode;
551 	int ret = 0;
552 
553 	for (i = 1; i <= MAX_BANDS; i++) {
554 		mode = wl_adps_get_mode(ndev, i);
555 		if (mode > 0) {
556 			ret |= (1 << i);
557 		}
558 	}
559 
560 	return ret;
561 }
562 
563 int
wl_adps_set_suspend(struct bcm_cfg80211 * cfg,struct net_device * ndev,uint8 suspend)564 wl_adps_set_suspend(struct bcm_cfg80211 *cfg, struct net_device *ndev, uint8 suspend)
565 {
566 	int ret = BCME_OK;
567 
568 	int buf_len;
569 	bcm_iov_buf_t *iov_buf = NULL;
570 	wl_adps_suspend_v1_t *data = NULL;
571 
572 	buf_len = OFFSETOF(bcm_iov_buf_t, data) + sizeof(*data);
573 	iov_buf = MALLOCZ(cfg->osh, buf_len);
574 	if (iov_buf == NULL) {
575 		WL_ERR(("%s - failed to alloc %d bytes for iov_buf\n",
576 			__FUNCTION__, buf_len));
577 		ret = BCME_NOMEM;
578 		goto exit;
579 	}
580 
581 	iov_buf->version = WL_ADPS_IOV_VER;
582 	iov_buf->len = sizeof(*data);
583 	iov_buf->id = WL_ADPS_IOV_SUSPEND;
584 
585 	data = (wl_adps_suspend_v1_t *)iov_buf->data;
586 	data->version = ADPS_SUB_IOV_VERSION_1;
587 	data->length = sizeof(*data);
588 	data->suspend = suspend;
589 
590 	ret = wldev_iovar_setbuf(ndev, "adps", (char *)iov_buf, buf_len,
591 		cfg->ioctl_buf, WLC_IOCTL_SMLEN, NULL);
592 	if (ret < 0) {
593 		if (ret == BCME_UNSUPPORTED) {
594 			WL_ERR(("%s - adps suspend is not supported\n", __FUNCTION__));
595 			ret = BCME_OK;
596 		}
597 		else {
598 			WL_ERR(("%s - fail to set adps suspend %d (%d)\n",
599 				__FUNCTION__, suspend, ret));
600 		}
601 		goto exit;
602 	}
603 	WL_INFORM_MEM(("[%s] Detect BAD AP and Suspend ADPS\n",	ndev->name));
604 exit:
605 	if (iov_buf) {
606 		MFREE(cfg->osh, iov_buf, buf_len);
607 	}
608 	return ret;
609 }
610 
611 bool
wl_adps_bad_ap_check(struct bcm_cfg80211 * cfg,const struct ether_addr * bssid)612 wl_adps_bad_ap_check(struct bcm_cfg80211 *cfg, const struct ether_addr *bssid)
613 {
614 #if !defined(DHD_ADPS_BAM_EXPORT)
615 	/* Update Bad AP list */
616 	if (list_empty(&cfg->bad_ap_mngr.list)) {
617 		wl_bad_ap_mngr_fread(cfg, WL_BAD_AP_INFO_FILE_PATH);
618 	}
619 #endif	/* DHD_ADPS_BAM_EXPORT */
620 
621 	if (wl_bad_ap_mngr_find(&cfg->bad_ap_mngr, bssid) != NULL)
622 		return TRUE;
623 
624 	return FALSE;
625 }
626 
627 s32
wl_adps_event_handler(struct bcm_cfg80211 * cfg,bcm_struct_cfgdev * cfgdev,const wl_event_msg_t * e,void * data)628 wl_adps_event_handler(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
629 	const wl_event_msg_t *e, void *data)
630 {
631 	int ret = BCME_OK;
632 	wl_event_adps_t *event_data = (wl_event_adps_t *)data;
633 
634 	switch (event_data->type) {
635 	case WL_E_TYPE_ADPS_BAD_AP:
636 		ret = wl_event_adps_bad_ap_mngr(cfg, data);
637 		break;
638 	default:
639 		break;
640 	}
641 
642 	return ret;
643 }
644