2 * Bad AP Manager for ADPS
4 * Copyright (C) 1999-2019, Broadcom.
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:
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.
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
25 * <<Broadcom-WL-IPTag/Open:>>
27 * $Id: wl_bam.c 790523 2018-11-26 08:24:06Z $
30 #include <linux/time.h>
31 #include <linux/list_sort.h>
32 #include <wl_cfg80211.h>
34 #include <wldev_common.h>
38 wl_bad_ap_mngr_add_entry(wl_bad_ap_mngr_t
*bad_ap_mngr
, wl_bad_ap_info_t
*bad_ap_info
)
41 wl_bad_ap_info_entry_t
*entry
;
43 entry
= MALLOCZ(bad_ap_mngr
->osh
, sizeof(*entry
));
45 WL_ERR(("%s: allocation for list failed\n", __FUNCTION__
));
49 memcpy(&entry
->bad_ap
, bad_ap_info
, sizeof(entry
->bad_ap
));
50 INIT_LIST_HEAD(&entry
->list
);
51 spin_lock_irqsave(&bad_ap_mngr
->lock
, flags
);
52 list_add_tail(&entry
->list
, &bad_ap_mngr
->list
);
53 spin_unlock_irqrestore(&bad_ap_mngr
->lock
, flags
);
60 #if !defined(DHD_ADPS_BAM_EXPORT)
61 #define WL_BAD_AP_INFO_FILE_PATH WL_BAM_FILE_PATH".bad_ap_list.info"
62 #define WL_BAD_AP_MAX_BUF_SIZE 1024u
64 /* Bad AP information data format
66 * Status and Reason: come from event
67 * Connection count: Increase connecting Bad AP
69 * BSSID,year-month-day hour:min:sec,Status,Reason,Connection count
70 * ex) XX:XX:XX:XX:XX:XX,1970-01-01 00:00:00,1,2,1
73 #define WL_BAD_AP_INFO_FMT \
74 "%02x:%02x:%02x:%02x:%02x:%02x,%04ld-%02d-%02d %02d:%02d:%02d,%u,%u,%u\n"
75 #define WL_BAD_AP_INFO_FMT_ITEM_CNT 15u
78 wl_bad_ap_mngr_tm2ts(struct timespec
*ts
, const struct tm tm
)
80 ts
->tv_sec
= mktime(tm
.tm_year
, tm
.tm_mon
, tm
.tm_mday
, tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
85 wl_bad_ap_mngr_timecmp(void *priv
, struct list_head
*a
, struct list_head
*b
)
92 wl_bad_ap_info_entry_t
*e1
= container_of(a
, wl_bad_ap_info_entry_t
, list
);
93 wl_bad_ap_info_entry_t
*e2
= container_of(b
, wl_bad_ap_info_entry_t
, list
);
95 wl_bad_ap_mngr_tm2ts(&ts1
, e1
->bad_ap
.tm
);
96 wl_bad_ap_mngr_tm2ts(&ts2
, e2
->bad_ap
.tm
);
98 ret
= timespec_compare((const struct timespec
*)&ts1
, (const struct timespec
*)&ts2
);
104 wl_bad_ap_mngr_update(struct bcm_cfg80211
*cfg
, wl_bad_ap_info_t
*bad_ap_info
)
106 wl_bad_ap_info_entry_t
*entry
;
109 if (list_empty(&cfg
->bad_ap_mngr
.list
)) {
113 spin_lock_irqsave(&cfg
->bad_ap_mngr
.lock
, flags
);
114 /* sort by timestamp */
115 list_sort(NULL
, &cfg
->bad_ap_mngr
.list
, wl_bad_ap_mngr_timecmp
);
117 /* update entry with the latest bad ap information */
118 entry
= list_first_entry(&cfg
->bad_ap_mngr
.list
, wl_bad_ap_info_entry_t
, list
);
120 memcpy(&entry
->bad_ap
, bad_ap_info
, sizeof(entry
->bad_ap
));
122 spin_unlock_irqrestore(&cfg
->bad_ap_mngr
.lock
, flags
);
126 wl_bad_ap_mngr_fread_bad_ap_info(char *buf
, int buf_len
, wl_bad_ap_info_t
*bad_ap
)
128 return snprintf(buf
, buf_len
, WL_BAD_AP_INFO_FMT
,
129 bad_ap
->bssid
.octet
[0], bad_ap
->bssid
.octet
[1],
130 bad_ap
->bssid
.octet
[2], bad_ap
->bssid
.octet
[3],
131 bad_ap
->bssid
.octet
[4], bad_ap
->bssid
.octet
[5],
132 bad_ap
->tm
.tm_year
+ 1900, bad_ap
->tm
.tm_mon
+ 1, bad_ap
->tm
.tm_mday
,
133 bad_ap
->tm
.tm_hour
, bad_ap
->tm
.tm_min
, bad_ap
->tm
.tm_sec
,
134 bad_ap
->status
, bad_ap
->reason
, bad_ap
->connect_count
);
138 wl_bad_ap_mngr_fparse(struct bcm_cfg80211
*cfg
, struct file
*fp
)
143 int ret
= BCME_ERROR
;
145 wl_bad_ap_info_t bad_ap
;
148 buf
= MALLOCZ(cfg
->osh
, WL_BAD_AP_MAX_BUF_SIZE
);
150 WL_ERR(("%s: allocation for buf failed\n", __FUNCTION__
));
154 ret
= vfs_read(fp
, buf
, WL_BAD_AP_MAX_BUF_SIZE
, &fp
->f_pos
);
156 WL_ERR(("%s: file read failed (%d)\n", __FUNCTION__
, ret
));
162 ret
= sscanf(&buf
[pos
], WL_BAD_AP_INFO_FMT
,
163 (uint32
*)&bad_ap
.bssid
.octet
[0], (uint32
*)&bad_ap
.bssid
.octet
[1],
164 (uint32
*)&bad_ap
.bssid
.octet
[2], (uint32
*)&bad_ap
.bssid
.octet
[3],
165 (uint32
*)&bad_ap
.bssid
.octet
[4], (uint32
*)&bad_ap
.bssid
.octet
[5],
166 (long int *)&bad_ap
.tm
.tm_year
, (uint32
*)&bad_ap
.tm
.tm_mon
,
167 (uint32
*)&bad_ap
.tm
.tm_mday
, (uint32
*)&bad_ap
.tm
.tm_hour
,
168 (uint32
*)&bad_ap
.tm
.tm_min
, (uint32
*)&bad_ap
.tm
.tm_sec
,
169 (uint32
*)&bad_ap
.status
, (uint32
*)&bad_ap
.reason
,
170 (uint32
*)&bad_ap
.connect_count
);
171 if (ret
!= WL_BAD_AP_INFO_FMT_ITEM_CNT
) {
172 WL_ERR(("%s: file parse failed(expected: %d actual: %d)\n",
173 __FUNCTION__
, WL_BAD_AP_INFO_FMT_ITEM_CNT
, ret
));
178 /* convert struct tm format */
179 bad_ap
.tm
.tm_year
-= 1900;
180 bad_ap
.tm
.tm_mon
-= 1;
182 ret
= wl_bad_ap_mngr_add(&cfg
->bad_ap_mngr
, &bad_ap
);
184 WL_ERR(("%s: bad ap add failed\n", __FUNCTION__
));
188 ret
= wl_bad_ap_mngr_fread_bad_ap_info(tmp
, sizeof(tmp
), &bad_ap
);
190 WL_ERR(("%s: wl_bad_ap_mngr_fread_bad_ap_info failed (%d)\n",
195 if (cfg
->bad_ap_mngr
.num
>= WL_BAD_AP_MAX_ENTRY_NUM
) {
207 MFREE(cfg
->osh
, buf
, WL_BAD_AP_MAX_BUF_SIZE
);
214 wl_bad_ap_mngr_fread(struct bcm_cfg80211
*cfg
, const char *fname
)
216 int ret
= BCME_ERROR
;
219 struct file
*fp
= NULL
;
222 WL_ERR(("%s: fname is NULL\n", __FUNCTION__
));
225 mutex_lock(&cfg
->bad_ap_mngr
.fs_lock
);
230 fp
= filp_open(fname
, O_RDONLY
, 0);
233 WL_ERR(("%s: file open failed(%d)\n", __FUNCTION__
, ret
));
237 if ((ret
= wl_bad_ap_mngr_fparse(cfg
, fp
)) < 0) {
242 filp_close(fp
, NULL
);
246 mutex_unlock(&cfg
->bad_ap_mngr
.fs_lock
);
252 wl_bad_ap_mngr_fwrite(struct bcm_cfg80211
*cfg
, const char *fname
)
254 int ret
= BCME_ERROR
;
257 struct file
*fp
= NULL
;
260 char tmp
[WL_BAD_AP_MAX_BUF_SIZE
];
261 wl_bad_ap_info_t
*bad_ap
;
262 wl_bad_ap_info_entry_t
*entry
;
264 if (list_empty(&cfg
->bad_ap_mngr
.list
)) {
269 return BCME_NOTFOUND
;
272 mutex_lock(&cfg
->bad_ap_mngr
.fs_lock
);
277 fp
= filp_open(fname
, O_CREAT
| O_RDWR
| O_TRUNC
, 0666);
280 WL_ERR(("%s: file open failed(%d)\n", __FUNCTION__
, ret
));
285 memset(tmp
, 0, sizeof(tmp
));
286 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
287 #pragma GCC diagnostic push
288 #pragma GCC diagnostic ignored "-Wcast-qual"
290 list_for_each_entry(entry
, &cfg
->bad_ap_mngr
.list
, list
) {
291 bad_ap
= &entry
->bad_ap
;
292 ret
= wl_bad_ap_mngr_fread_bad_ap_info(&tmp
[len
], sizeof(tmp
) - len
, bad_ap
);
294 WL_ERR(("%s: snprintf failed(%d)\n", __FUNCTION__
, ret
));
300 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
301 #pragma GCC diagnostic pop
304 ret
= vfs_write(fp
, tmp
, len
, &fp
->f_pos
);
306 WL_ERR(("%s: file write failed(%d)\n", __FUNCTION__
, ret
));
309 /* Sync file from filesystem to physical media */
310 ret
= vfs_fsync(fp
, 0);
312 WL_ERR(("%s: sync file failed(%d)\n", __FUNCTION__
, ret
));
318 filp_close(fp
, NULL
);
321 mutex_unlock(&cfg
->bad_ap_mngr
.fs_lock
);
326 extern wl_bad_ap_mngr_t
*g_bad_ap_mngr
;
327 #endif /* DHD_ADPS_BAM_EXPORT */
329 wl_bad_ap_info_entry_t
*
330 wl_bad_ap_mngr_find(wl_bad_ap_mngr_t
*bad_ap_mngr
, const struct ether_addr
*bssid
)
332 wl_bad_ap_info_entry_t
*entry
;
335 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
336 #pragma GCC diagnostic push
337 #pragma GCC diagnostic ignored "-Wcast-qual"
339 spin_lock_irqsave(&bad_ap_mngr
->lock
, flags
);
340 list_for_each_entry(entry
, &bad_ap_mngr
->list
, list
) {
341 if (!memcmp(&entry
->bad_ap
.bssid
.octet
, bssid
->octet
, ETHER_ADDR_LEN
)) {
342 spin_unlock_irqrestore(&bad_ap_mngr
->lock
, flags
);
346 spin_unlock_irqrestore(&bad_ap_mngr
->lock
, flags
);
347 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
348 #pragma GCC diagnostic pop
354 wl_bad_ap_mngr_add(wl_bad_ap_mngr_t
*bad_ap_mngr
, wl_bad_ap_info_t
*bad_ap_info
)
357 wl_bad_ap_info_entry_t
*entry
;
360 BCM_REFERENCE(entry
);
361 BCM_REFERENCE(flags
);
363 #if !defined(DHD_ADPS_BAM_EXPORT)
364 ret
= wl_bad_ap_mngr_add_entry(bad_ap_mngr
, bad_ap_info
);
366 if (bad_ap_mngr
->num
== WL_BAD_AP_MAX_ENTRY_NUM
) {
367 /* Remove the oldest entry if entry list is full */
368 spin_lock_irqsave(&bad_ap_mngr
->lock
, flags
);
369 list_del(bad_ap_mngr
->list
.next
);
371 spin_unlock_irqrestore(&bad_ap_mngr
->lock
, flags
);
374 /* delete duplicated entry to update it at tail to keep the odrer */
375 entry
= wl_bad_ap_mngr_find(bad_ap_mngr
, &bad_ap_info
->bssid
);
377 spin_lock_irqsave(&bad_ap_mngr
->lock
, flags
);
378 list_del(&entry
->list
);
380 spin_unlock_irqrestore(&bad_ap_mngr
->lock
, flags
);
383 ret
= wl_bad_ap_mngr_add_entry(bad_ap_mngr
, bad_ap_info
);
385 WL_ERR(("%s - fail to add bad ap data(%d)\n", __FUNCTION__
, ret
));
388 #endif /* DHD_ADPS_BAM_EXPORT */
393 wl_bad_ap_mngr_deinit(struct bcm_cfg80211
*cfg
)
395 wl_bad_ap_info_entry_t
*entry
;
398 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
399 #pragma GCC diagnostic push
400 #pragma GCC diagnostic ignored "-Wcast-qual"
402 spin_lock_irqsave(&cfg
->bad_ap_mngr
.lock
, flags
);
403 while (!list_empty(&cfg
->bad_ap_mngr
.list
)) {
404 entry
= list_entry(cfg
->bad_ap_mngr
.list
.next
, wl_bad_ap_info_entry_t
, list
);
406 list_del(&cfg
->bad_ap_mngr
.list
);
407 MFREE(cfg
->osh
, entry
, sizeof(*entry
));
410 spin_unlock_irqrestore(&cfg
->bad_ap_mngr
.lock
, flags
);
411 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
412 #pragma GCC diagnostic pop
414 #if !defined(DHD_ADPS_BAM_EXPORT)
415 mutex_destroy(&cfg
->bad_ap_mngr
.fs_lock
);
416 #endif /* !DHD_ADPS_BAM_EXPORT */
420 wl_bad_ap_mngr_init(struct bcm_cfg80211
*cfg
)
422 cfg
->bad_ap_mngr
.osh
= cfg
->osh
;
423 cfg
->bad_ap_mngr
.num
= 0;
425 spin_lock_init(&cfg
->bad_ap_mngr
.lock
);
426 INIT_LIST_HEAD(&cfg
->bad_ap_mngr
.list
);
428 #if !defined(DHD_ADPS_BAM_EXPORT)
429 mutex_init(&cfg
->bad_ap_mngr
.fs_lock
);
431 g_bad_ap_mngr
= &cfg
->bad_ap_mngr
;
432 #endif /* !DHD_ADPS_BAM_EXPORT */
436 wl_event_adps_bad_ap_mngr(struct bcm_cfg80211
*cfg
, void *data
)
440 wl_event_adps_t
*event_data
= (wl_event_adps_t
*)data
;
441 wl_event_adps_bad_ap_t
*bad_ap_data
;
443 wl_bad_ap_info_entry_t
*entry
;
444 wl_bad_ap_info_t temp
;
445 #if !defined(DHD_ADPS_BAM_EXPORT)
447 #endif /* !DHD_ADPS_BAM_EXPORT */
449 if (event_data
->version
!= WL_EVENT_ADPS_VER_1
) {
453 if (event_data
->length
!= (OFFSETOF(wl_event_adps_t
, data
) + sizeof(*bad_ap_data
))) {
458 BCM_REFERENCE(entry
);
459 bad_ap_data
= (wl_event_adps_bad_ap_t
*)event_data
->data
;
461 #if !defined(DHD_ADPS_BAM_EXPORT)
462 /* Update Bad AP list */
463 if (list_empty(&cfg
->bad_ap_mngr
.list
)) {
464 wl_bad_ap_mngr_fread(cfg
, WL_BAD_AP_INFO_FILE_PATH
);
468 entry
= wl_bad_ap_mngr_find(&cfg
->bad_ap_mngr
, &bad_ap_data
->ea
);
470 time_to_tm((ts
.tv_sec
- (sys_tz
.tz_minuteswest
* 60)), 0, &entry
->bad_ap
.tm
);
471 entry
->bad_ap
.status
= bad_ap_data
->status
;
472 entry
->bad_ap
.reason
= bad_ap_data
->reason
;
473 entry
->bad_ap
.connect_count
++;
476 time_to_tm((ts
.tv_sec
- (sys_tz
.tz_minuteswest
* 60)), 0, &temp
.tm
);
477 temp
.status
= bad_ap_data
->status
;
478 temp
.reason
= bad_ap_data
->reason
;
479 temp
.connect_count
= 1;
480 memcpy(temp
.bssid
.octet
, &bad_ap_data
->ea
.octet
, ETHER_ADDR_LEN
);
482 if (cfg
->bad_ap_mngr
.num
< WL_BAD_AP_MAX_ENTRY_NUM
) {
483 wl_bad_ap_mngr_add(&cfg
->bad_ap_mngr
, &temp
);
486 wl_bad_ap_mngr_update(cfg
, &temp
);
490 wl_bad_ap_mngr_fwrite(cfg
, WL_BAD_AP_INFO_FILE_PATH
);
492 memcpy(temp
.bssid
.octet
, &bad_ap_data
->ea
.octet
, ETHER_ADDR_LEN
);
493 ret
= wl_bad_ap_mngr_add(&cfg
->bad_ap_mngr
, &temp
);
494 #endif /* !DHD_ADPS_BAM_EXPORT */
502 * Enabled: WLC_BAND_2G, WLC_BAND_5G, WLC_BAND_ALL
506 wl_adps_enabled(struct bcm_cfg80211
*cfg
, struct net_device
*ndev
)
514 char buf
[WLC_IOCTL_SMLEN
];
516 bcm_iov_buf_t iov_buf
;
518 wl_adps_params_v1_t
*data
= NULL
;
520 memset(&iov_buf
, 0, sizeof(iov_buf
));
521 len
= OFFSETOF(bcm_iov_buf_t
, data
) + sizeof(band
);
523 iov_buf
.version
= WL_ADPS_IOV_VER
;
524 iov_buf
.len
= sizeof(band
);
525 iov_buf
.id
= WL_ADPS_IOV_MODE
;
528 pdata
= (uint8
*)iov_buf
.data
;
529 for (i
= 1; i
<= MAX_BANDS
; i
++) {
531 ret
= wldev_iovar_getbuf(ndev
, "adps", &iov_buf
, len
, buf
, WLC_IOCTL_SMLEN
, NULL
);
533 WL_ERR(("%s - fail to get adps for band %d (%d)\n",
534 __FUNCTION__
, i
, ret
));
537 resp
= (bcm_iov_buf_t
*)buf
;
538 data
= (wl_adps_params_v1_t
*)resp
->data
;
541 if (data
->band
== NL80211_BAND_2GHZ
) {
544 else if (data
->band
== NL80211_BAND_5GHZ
) {
554 wl_adps_set_suspend(struct bcm_cfg80211
*cfg
, struct net_device
*ndev
, uint8 suspend
)
559 bcm_iov_buf_t
*iov_buf
= NULL
;
560 wl_adps_suspend_v1_t
*data
= NULL
;
562 buf_len
= OFFSETOF(bcm_iov_buf_t
, data
) + sizeof(*data
);
563 iov_buf
= MALLOCZ(cfg
->osh
, buf_len
);
564 if (iov_buf
== NULL
) {
565 WL_ERR(("%s - failed to alloc %d bytes for iov_buf\n",
566 __FUNCTION__
, buf_len
));
571 iov_buf
->version
= WL_ADPS_IOV_VER
;
572 iov_buf
->len
= buf_len
;
573 iov_buf
->id
= WL_ADPS_IOV_SUSPEND
;
575 data
= (wl_adps_suspend_v1_t
*)iov_buf
->data
;
576 data
->version
= ADPS_SUB_IOV_VERSION_1
;
577 data
->length
= sizeof(*data
);
578 data
->suspend
= suspend
;
580 ret
= wldev_iovar_setbuf(ndev
, "adps", (char *)iov_buf
, buf_len
,
581 cfg
->ioctl_buf
, WLC_IOCTL_SMLEN
, NULL
);
583 if (ret
== BCME_UNSUPPORTED
) {
584 WL_ERR(("%s - adps suspend is not supported\n", __FUNCTION__
));
588 WL_ERR(("%s - fail to set adps suspend %d (%d)\n",
589 __FUNCTION__
, suspend
, ret
));
593 WL_INFORM_MEM(("[%s] Detect BAD AP and Suspend ADPS\n", ndev
->name
));
596 MFREE(cfg
->osh
, iov_buf
, buf_len
);
602 wl_adps_bad_ap_check(struct bcm_cfg80211
*cfg
, const struct ether_addr
*bssid
)
604 #if !defined(DHD_ADPS_BAM_EXPORT)
605 /* Update Bad AP list */
606 if (list_empty(&cfg
->bad_ap_mngr
.list
)) {
607 wl_bad_ap_mngr_fread(cfg
, WL_BAD_AP_INFO_FILE_PATH
);
609 #endif /* DHD_ADPS_BAM_EXPORT */
611 if (wl_bad_ap_mngr_find(&cfg
->bad_ap_mngr
, bssid
) != NULL
)
618 wl_adps_event_handler(struct bcm_cfg80211
*cfg
, bcm_struct_cfgdev
*cfgdev
,
619 const wl_event_msg_t
*e
, void *data
)
622 wl_event_adps_t
*event_data
= (wl_event_adps_t
*)data
;
624 switch (event_data
->type
) {
625 case WL_E_TYPE_ADPS_BAD_AP
:
626 ret
= wl_event_adps_bad_ap_mngr(cfg
, data
);