Commit | Line | Data |
---|---|---|
1cac41cb MB |
1 | /* |
2 | * Bad AP Manager for ADPS | |
3 | * | |
4 | * Copyright (C) 1999-2019, 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 | * 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. | |
23 | * | |
24 | * | |
25 | * <<Broadcom-WL-IPTag/Open:>> | |
26 | * | |
27 | * $Id: wl_bam.c 790523 2018-11-26 08:24:06Z $ | |
28 | */ | |
29 | #include <bcmiov.h> | |
30 | #include <linux/time.h> | |
31 | #include <linux/list_sort.h> | |
32 | #include <wl_cfg80211.h> | |
33 | #include <wlioctl.h> | |
34 | #include <wldev_common.h> | |
35 | #include <wl_bam.h> | |
36 | ||
37 | static int | |
38 | wl_bad_ap_mngr_add_entry(wl_bad_ap_mngr_t *bad_ap_mngr, wl_bad_ap_info_t *bad_ap_info) | |
39 | { | |
40 | unsigned long flags; | |
41 | wl_bad_ap_info_entry_t *entry; | |
42 | ||
43 | entry = MALLOCZ(bad_ap_mngr->osh, sizeof(*entry)); | |
44 | if (entry == NULL) { | |
45 | WL_ERR(("%s: allocation for list failed\n", __FUNCTION__)); | |
46 | return BCME_NOMEM; | |
47 | } | |
48 | ||
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); | |
54 | ||
55 | bad_ap_mngr->num++; | |
56 | ||
57 | return BCME_OK; | |
58 | } | |
59 | ||
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 | |
63 | ||
64 | /* Bad AP information data format | |
65 | * | |
66 | * Status and Reason: come from event | |
67 | * Connection count: Increase connecting Bad AP | |
68 | * | |
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 | |
71 | * | |
72 | */ | |
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 | |
76 | ||
77 | static inline void | |
78 | wl_bad_ap_mngr_tm2ts(struct timespec *ts, const struct tm tm) | |
79 | { | |
80 | ts->tv_sec = mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); | |
81 | ts->tv_nsec = 0; | |
82 | } | |
83 | ||
84 | static int | |
85 | wl_bad_ap_mngr_timecmp(void *priv, struct list_head *a, struct list_head *b) | |
86 | { | |
87 | int ret; | |
88 | ||
89 | struct timespec ts1; | |
90 | struct timespec ts2; | |
91 | ||
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); | |
94 | ||
95 | wl_bad_ap_mngr_tm2ts(&ts1, e1->bad_ap.tm); | |
96 | wl_bad_ap_mngr_tm2ts(&ts2, e2->bad_ap.tm); | |
97 | ||
98 | ret = timespec_compare((const struct timespec *)&ts1, (const struct timespec *)&ts2); | |
99 | ||
100 | return ret; | |
101 | } | |
102 | ||
103 | static void | |
104 | wl_bad_ap_mngr_update(struct bcm_cfg80211 *cfg, wl_bad_ap_info_t *bad_ap_info) | |
105 | { | |
106 | wl_bad_ap_info_entry_t *entry; | |
107 | unsigned long flags; | |
108 | ||
109 | if (list_empty(&cfg->bad_ap_mngr.list)) { | |
110 | return; | |
111 | } | |
112 | ||
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); | |
116 | ||
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); | |
119 | if (entry != NULL) { | |
120 | memcpy(&entry->bad_ap, bad_ap_info, sizeof(entry->bad_ap)); | |
121 | } | |
122 | spin_unlock_irqrestore(&cfg->bad_ap_mngr.lock, flags); | |
123 | } | |
124 | ||
125 | static inline int | |
126 | wl_bad_ap_mngr_fread_bad_ap_info(char *buf, int buf_len, wl_bad_ap_info_t *bad_ap) | |
127 | { | |
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); | |
135 | } | |
136 | ||
137 | static int | |
138 | wl_bad_ap_mngr_fparse(struct bcm_cfg80211 *cfg, struct file *fp) | |
139 | { | |
140 | int len; | |
141 | int pos = 0; | |
142 | char tmp[128]; | |
143 | int ret = BCME_ERROR; | |
144 | ||
145 | wl_bad_ap_info_t bad_ap; | |
146 | char *buf = NULL; | |
147 | ||
148 | buf = MALLOCZ(cfg->osh, WL_BAD_AP_MAX_BUF_SIZE); | |
149 | if (buf == NULL) { | |
150 | WL_ERR(("%s: allocation for buf failed\n", __FUNCTION__)); | |
151 | return BCME_NOMEM; | |
152 | } | |
153 | ||
154 | ret = vfs_read(fp, buf, WL_BAD_AP_MAX_BUF_SIZE, &fp->f_pos); | |
155 | if (ret < 0) { | |
156 | WL_ERR(("%s: file read failed (%d)\n", __FUNCTION__, ret)); | |
157 | goto fail; | |
158 | } | |
159 | ||
160 | len = ret; | |
161 | do { | |
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)); | |
174 | ret = BCME_ERROR; | |
175 | goto fail; | |
176 | } | |
177 | ||
178 | /* convert struct tm format */ | |
179 | bad_ap.tm.tm_year -= 1900; | |
180 | bad_ap.tm.tm_mon -= 1; | |
181 | ||
182 | ret = wl_bad_ap_mngr_add(&cfg->bad_ap_mngr, &bad_ap); | |
183 | if (ret < 0) { | |
184 | WL_ERR(("%s: bad ap add failed\n", __FUNCTION__)); | |
185 | goto fail; | |
186 | } | |
187 | ||
188 | ret = wl_bad_ap_mngr_fread_bad_ap_info(tmp, sizeof(tmp), &bad_ap); | |
189 | if (ret < 0) { | |
190 | WL_ERR(("%s: wl_bad_ap_mngr_fread_bad_ap_info failed (%d)\n", | |
191 | __FUNCTION__, ret)); | |
192 | goto fail; | |
193 | } | |
194 | ||
195 | if (cfg->bad_ap_mngr.num >= WL_BAD_AP_MAX_ENTRY_NUM) { | |
196 | break; | |
197 | } | |
198 | ||
199 | len -= ret; | |
200 | pos += ret; | |
201 | } while (len > 0); | |
202 | ||
203 | ret = BCME_OK; | |
204 | ||
205 | fail: | |
206 | if (buf) { | |
207 | MFREE(cfg->osh, buf, WL_BAD_AP_MAX_BUF_SIZE); | |
208 | } | |
209 | ||
210 | return ret; | |
211 | } | |
212 | ||
213 | static int | |
214 | wl_bad_ap_mngr_fread(struct bcm_cfg80211 *cfg, const char *fname) | |
215 | { | |
216 | int ret = BCME_ERROR; | |
217 | ||
218 | mm_segment_t fs; | |
219 | struct file *fp = NULL; | |
220 | ||
221 | if (fname == NULL) { | |
222 | WL_ERR(("%s: fname is NULL\n", __FUNCTION__)); | |
223 | return ret; | |
224 | } | |
225 | mutex_lock(&cfg->bad_ap_mngr.fs_lock); | |
226 | ||
227 | fs = get_fs(); | |
228 | set_fs(KERNEL_DS); | |
229 | ||
230 | fp = filp_open(fname, O_RDONLY, 0); | |
231 | if (IS_ERR(fp)) { | |
232 | fp = NULL; | |
233 | WL_ERR(("%s: file open failed(%d)\n", __FUNCTION__, ret)); | |
234 | goto fail; | |
235 | } | |
236 | ||
237 | if ((ret = wl_bad_ap_mngr_fparse(cfg, fp)) < 0) { | |
238 | goto fail; | |
239 | } | |
240 | fail: | |
241 | if (fp) { | |
242 | filp_close(fp, NULL); | |
243 | } | |
244 | set_fs(fs); | |
245 | ||
246 | mutex_unlock(&cfg->bad_ap_mngr.fs_lock); | |
247 | ||
248 | return ret; | |
249 | } | |
250 | ||
251 | static int | |
252 | wl_bad_ap_mngr_fwrite(struct bcm_cfg80211 *cfg, const char *fname) | |
253 | { | |
254 | int ret = BCME_ERROR; | |
255 | ||
256 | mm_segment_t fs; | |
257 | struct file *fp = NULL; | |
258 | ||
259 | int len = 0; | |
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; | |
263 | ||
264 | if (list_empty(&cfg->bad_ap_mngr.list)) { | |
265 | return BCME_ERROR; | |
266 | } | |
267 | ||
268 | if (fname == NULL) { | |
269 | return BCME_NOTFOUND; | |
270 | } | |
271 | ||
272 | mutex_lock(&cfg->bad_ap_mngr.fs_lock); | |
273 | ||
274 | fs = get_fs(); | |
275 | set_fs(KERNEL_DS); | |
276 | ||
277 | fp = filp_open(fname, O_CREAT | O_RDWR | O_TRUNC, 0666); | |
278 | if (IS_ERR(fp)) { | |
279 | ret = PTR_ERR(fp); | |
280 | WL_ERR(("%s: file open failed(%d)\n", __FUNCTION__, ret)); | |
281 | fp = NULL; | |
282 | goto fail; | |
283 | } | |
284 | ||
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" | |
289 | #endif // endif | |
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); | |
293 | if (ret < 0) { | |
294 | WL_ERR(("%s: snprintf failed(%d)\n", __FUNCTION__, ret)); | |
295 | goto fail; | |
296 | } | |
297 | ||
298 | len += ret; | |
299 | } | |
300 | #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) | |
301 | #pragma GCC diagnostic pop | |
302 | #endif // endif | |
303 | ||
304 | ret = vfs_write(fp, tmp, len, &fp->f_pos); | |
305 | if (ret < 0) { | |
306 | WL_ERR(("%s: file write failed(%d)\n", __FUNCTION__, ret)); | |
307 | goto fail; | |
308 | } | |
309 | /* Sync file from filesystem to physical media */ | |
310 | ret = vfs_fsync(fp, 0); | |
311 | if (ret < 0) { | |
312 | WL_ERR(("%s: sync file failed(%d)\n", __FUNCTION__, ret)); | |
313 | goto fail; | |
314 | } | |
315 | ret = BCME_OK; | |
316 | fail: | |
317 | if (fp) { | |
318 | filp_close(fp, NULL); | |
319 | } | |
320 | set_fs(fs); | |
321 | mutex_unlock(&cfg->bad_ap_mngr.fs_lock); | |
322 | ||
323 | return ret; | |
324 | } | |
325 | #else | |
326 | extern wl_bad_ap_mngr_t *g_bad_ap_mngr; | |
327 | #endif /* DHD_ADPS_BAM_EXPORT */ | |
328 | ||
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) | |
331 | { | |
332 | wl_bad_ap_info_entry_t *entry; | |
333 | unsigned long flags; | |
334 | ||
335 | #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) | |
336 | #pragma GCC diagnostic push | |
337 | #pragma GCC diagnostic ignored "-Wcast-qual" | |
338 | #endif // endif | |
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); | |
343 | return entry; | |
344 | } | |
345 | } | |
346 | spin_unlock_irqrestore(&bad_ap_mngr->lock, flags); | |
347 | #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) | |
348 | #pragma GCC diagnostic pop | |
349 | #endif // endif | |
350 | return NULL; | |
351 | } | |
352 | ||
353 | int | |
354 | wl_bad_ap_mngr_add(wl_bad_ap_mngr_t *bad_ap_mngr, wl_bad_ap_info_t *bad_ap_info) | |
355 | { | |
356 | int ret; | |
357 | wl_bad_ap_info_entry_t *entry; | |
358 | unsigned long flags; | |
359 | ||
360 | BCM_REFERENCE(entry); | |
361 | BCM_REFERENCE(flags); | |
362 | ||
363 | #if !defined(DHD_ADPS_BAM_EXPORT) | |
364 | ret = wl_bad_ap_mngr_add_entry(bad_ap_mngr, bad_ap_info); | |
365 | #else | |
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); | |
370 | bad_ap_mngr->num--; | |
371 | spin_unlock_irqrestore(&bad_ap_mngr->lock, flags); | |
372 | } | |
373 | ||
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); | |
376 | if (entry != NULL) { | |
377 | spin_lock_irqsave(&bad_ap_mngr->lock, flags); | |
378 | list_del(&entry->list); | |
379 | bad_ap_mngr->num--; | |
380 | spin_unlock_irqrestore(&bad_ap_mngr->lock, flags); | |
381 | } | |
382 | ||
383 | ret = wl_bad_ap_mngr_add_entry(bad_ap_mngr, bad_ap_info); | |
384 | if (ret < 0) { | |
385 | WL_ERR(("%s - fail to add bad ap data(%d)\n", __FUNCTION__, ret)); | |
386 | return ret; | |
387 | } | |
388 | #endif /* DHD_ADPS_BAM_EXPORT */ | |
389 | return ret; | |
390 | } | |
391 | ||
392 | void | |
393 | wl_bad_ap_mngr_deinit(struct bcm_cfg80211 *cfg) | |
394 | { | |
395 | wl_bad_ap_info_entry_t *entry; | |
396 | unsigned long flags; | |
397 | ||
398 | #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) | |
399 | #pragma GCC diagnostic push | |
400 | #pragma GCC diagnostic ignored "-Wcast-qual" | |
401 | #endif // endif | |
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); | |
405 | if (entry) { | |
406 | list_del(&cfg->bad_ap_mngr.list); | |
407 | MFREE(cfg->osh, entry, sizeof(*entry)); | |
408 | } | |
409 | } | |
410 | spin_unlock_irqrestore(&cfg->bad_ap_mngr.lock, flags); | |
411 | #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) | |
412 | #pragma GCC diagnostic pop | |
413 | #endif // endif | |
414 | #if !defined(DHD_ADPS_BAM_EXPORT) | |
415 | mutex_destroy(&cfg->bad_ap_mngr.fs_lock); | |
416 | #endif /* !DHD_ADPS_BAM_EXPORT */ | |
417 | } | |
418 | ||
419 | void | |
420 | wl_bad_ap_mngr_init(struct bcm_cfg80211 *cfg) | |
421 | { | |
422 | cfg->bad_ap_mngr.osh = cfg->osh; | |
423 | cfg->bad_ap_mngr.num = 0; | |
424 | ||
425 | spin_lock_init(&cfg->bad_ap_mngr.lock); | |
426 | INIT_LIST_HEAD(&cfg->bad_ap_mngr.list); | |
427 | ||
428 | #if !defined(DHD_ADPS_BAM_EXPORT) | |
429 | mutex_init(&cfg->bad_ap_mngr.fs_lock); | |
430 | #else | |
431 | g_bad_ap_mngr = &cfg->bad_ap_mngr; | |
432 | #endif /* !DHD_ADPS_BAM_EXPORT */ | |
433 | } | |
434 | ||
435 | static int | |
436 | wl_event_adps_bad_ap_mngr(struct bcm_cfg80211 *cfg, void *data) | |
437 | { | |
438 | int ret = BCME_OK; | |
439 | ||
440 | wl_event_adps_t *event_data = (wl_event_adps_t *)data; | |
441 | wl_event_adps_bad_ap_t *bad_ap_data; | |
442 | ||
443 | wl_bad_ap_info_entry_t *entry; | |
444 | wl_bad_ap_info_t temp; | |
445 | #if !defined(DHD_ADPS_BAM_EXPORT) | |
446 | struct timespec ts; | |
447 | #endif /* !DHD_ADPS_BAM_EXPORT */ | |
448 | ||
449 | if (event_data->version != WL_EVENT_ADPS_VER_1) { | |
450 | return BCME_VERSION; | |
451 | } | |
452 | ||
453 | if (event_data->length != (OFFSETOF(wl_event_adps_t, data) + sizeof(*bad_ap_data))) { | |
454 | return BCME_ERROR; | |
455 | } | |
456 | ||
457 | BCM_REFERENCE(ret); | |
458 | BCM_REFERENCE(entry); | |
459 | bad_ap_data = (wl_event_adps_bad_ap_t *)event_data->data; | |
460 | ||
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); | |
465 | } | |
466 | ||
467 | getnstimeofday(&ts); | |
468 | entry = wl_bad_ap_mngr_find(&cfg->bad_ap_mngr, &bad_ap_data->ea); | |
469 | if (entry != NULL) { | |
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++; | |
474 | } | |
475 | else { | |
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); | |
481 | ||
482 | if (cfg->bad_ap_mngr.num < WL_BAD_AP_MAX_ENTRY_NUM) { | |
483 | wl_bad_ap_mngr_add(&cfg->bad_ap_mngr, &temp); | |
484 | } | |
485 | else { | |
486 | wl_bad_ap_mngr_update(cfg, &temp); | |
487 | } | |
488 | } | |
489 | ||
490 | wl_bad_ap_mngr_fwrite(cfg, WL_BAD_AP_INFO_FILE_PATH); | |
491 | #else | |
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 */ | |
495 | ||
496 | return ret; | |
497 | } | |
498 | ||
499 | /* | |
500 | * Return value: | |
501 | * Disabled: 0 | |
502 | * Enabled: WLC_BAND_2G, WLC_BAND_5G, WLC_BAND_ALL | |
503 | * | |
504 | */ | |
505 | int | |
506 | wl_adps_enabled(struct bcm_cfg80211 *cfg, struct net_device *ndev) | |
507 | { | |
508 | int len; | |
509 | int ret = 0; | |
510 | ||
511 | uint8 i; | |
512 | uint8 band; | |
513 | uint8 *pdata; | |
514 | char buf[WLC_IOCTL_SMLEN]; | |
515 | ||
516 | bcm_iov_buf_t iov_buf; | |
517 | bcm_iov_buf_t *resp; | |
518 | wl_adps_params_v1_t *data = NULL; | |
519 | ||
520 | memset(&iov_buf, 0, sizeof(iov_buf)); | |
521 | len = OFFSETOF(bcm_iov_buf_t, data) + sizeof(band); | |
522 | ||
523 | iov_buf.version = WL_ADPS_IOV_VER; | |
524 | iov_buf.len = sizeof(band); | |
525 | iov_buf.id = WL_ADPS_IOV_MODE; | |
526 | ||
527 | band = 0; | |
528 | pdata = (uint8 *)iov_buf.data; | |
529 | for (i = 1; i <= MAX_BANDS; i++) { | |
530 | *pdata = i; | |
531 | ret = wldev_iovar_getbuf(ndev, "adps", &iov_buf, len, buf, WLC_IOCTL_SMLEN, NULL); | |
532 | if (ret < 0) { | |
533 | WL_ERR(("%s - fail to get adps for band %d (%d)\n", | |
534 | __FUNCTION__, i, ret)); | |
535 | return 0; | |
536 | } | |
537 | resp = (bcm_iov_buf_t *)buf; | |
538 | data = (wl_adps_params_v1_t *)resp->data; | |
539 | ||
540 | if (data->mode) { | |
92faf122 | 541 | if (data->band == NL80211_BAND_2GHZ) { |
1cac41cb MB |
542 | band += WLC_BAND_2G; |
543 | } | |
92faf122 | 544 | else if (data->band == NL80211_BAND_5GHZ) { |
1cac41cb MB |
545 | band += WLC_BAND_5G; |
546 | } | |
547 | } | |
548 | } | |
549 | ||
550 | return band; | |
551 | } | |
552 | ||
553 | int | |
554 | wl_adps_set_suspend(struct bcm_cfg80211 *cfg, struct net_device *ndev, uint8 suspend) | |
555 | { | |
556 | int ret = BCME_OK; | |
557 | ||
558 | int buf_len; | |
559 | bcm_iov_buf_t *iov_buf = NULL; | |
560 | wl_adps_suspend_v1_t *data = NULL; | |
561 | ||
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)); | |
567 | ret = BCME_NOMEM; | |
568 | goto exit; | |
569 | } | |
570 | ||
571 | iov_buf->version = WL_ADPS_IOV_VER; | |
572 | iov_buf->len = buf_len; | |
573 | iov_buf->id = WL_ADPS_IOV_SUSPEND; | |
574 | ||
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; | |
579 | ||
580 | ret = wldev_iovar_setbuf(ndev, "adps", (char *)iov_buf, buf_len, | |
581 | cfg->ioctl_buf, WLC_IOCTL_SMLEN, NULL); | |
582 | if (ret < 0) { | |
583 | if (ret == BCME_UNSUPPORTED) { | |
584 | WL_ERR(("%s - adps suspend is not supported\n", __FUNCTION__)); | |
585 | ret = BCME_OK; | |
586 | } | |
587 | else { | |
588 | WL_ERR(("%s - fail to set adps suspend %d (%d)\n", | |
589 | __FUNCTION__, suspend, ret)); | |
590 | } | |
591 | goto exit; | |
592 | } | |
593 | WL_INFORM_MEM(("[%s] Detect BAD AP and Suspend ADPS\n", ndev->name)); | |
594 | exit: | |
595 | if (iov_buf) { | |
596 | MFREE(cfg->osh, iov_buf, buf_len); | |
597 | } | |
598 | return ret; | |
599 | } | |
600 | ||
601 | bool | |
602 | wl_adps_bad_ap_check(struct bcm_cfg80211 *cfg, const struct ether_addr *bssid) | |
603 | { | |
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); | |
608 | } | |
609 | #endif /* DHD_ADPS_BAM_EXPORT */ | |
610 | ||
611 | if (wl_bad_ap_mngr_find(&cfg->bad_ap_mngr, bssid) != NULL) | |
612 | return TRUE; | |
613 | ||
614 | return FALSE; | |
615 | } | |
616 | ||
617 | s32 | |
618 | wl_adps_event_handler(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, | |
619 | const wl_event_msg_t *e, void *data) | |
620 | { | |
621 | int ret = BCME_OK; | |
622 | wl_event_adps_t *event_data = (wl_event_adps_t *)data; | |
623 | ||
624 | switch (event_data->type) { | |
625 | case WL_E_TYPE_ADPS_BAD_AP: | |
626 | ret = wl_event_adps_bad_ap_mngr(cfg, data); | |
627 | break; | |
628 | default: | |
629 | break; | |
630 | } | |
631 | ||
632 | return ret; | |
633 | } |