xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/wl_bigdata.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Bigdata logging and report. None EWP and Hang event.
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 <typedefs.h>
24 #include <osl.h>
25 #include <dngl_stats.h>
26 #include <bcmutils.h>
27 #include <dhd.h>
28 #include <dhd_dbg.h>
29 #include <wl_cfg80211.h>
30 #include <wldev_common.h>
31 #include <bcmendian.h>
32 #include <wlioctl.h>
33 #include <dhd_linux_wq.h>
34 #include <wl_bigdata.h>
35 
36 #define WL_AP_BIGDATA_LOG(args) WL_DBG(args)
37 
38 #define WLC_E_IS_ASSOC(e, r) \
39 	(((e == WLC_E_ASSOC_IND) || (e == WLC_E_REASSOC_IND)) && r == DOT11_SC_SUCCESS)
40 #define WLC_E_IS_DEAUTH(e) \
41 	(e == WLC_E_DISASSOC_IND || e == WLC_E_DEAUTH_IND || e == WLC_E_DEAUTH)
42 
43 static void dump_ap_stadata(wl_ap_sta_data_t *ap_sta_data);
44 static inline void copy_ap_stadata(wl_ap_sta_data_t *dest, wl_ap_sta_data_t *src);
45 static void wg_rate_dot11mode(uint32 *rate, uint8 *channel, uint32 *mode_80211);
46 static void wg_ht_mimo_ant(uint32 *nss, wl_rateset_args_t *rateset);
47 static void wg_vht_mimo_ant(uint32 *nss, wl_rateset_args_t *rateset);
48 #if defined(WL11AX)
49 static void wg_he_mimo_ant(uint32 *nss, uint16 *mcsset);
50 #endif /* WL11AX */
51 static int wg_parse_ap_stadata(struct net_device *dev, struct ether_addr *sta_mac,
52 		wl_ap_sta_data_t *ap_sta_data);
53 
54 static void
dump_ap_stadata(wl_ap_sta_data_t * ap_sta_data)55 dump_ap_stadata(wl_ap_sta_data_t *ap_sta_data)
56 {
57 	int i;
58 
59 	if (!ap_sta_data) {
60 		WL_AP_BIGDATA_LOG(("ap_sta_data is NULL\n"));
61 		return;
62 	}
63 
64 	for (i = 0; i < MAX_STA_INFO_AP_CNT; i++) {
65 		if (!ap_sta_data[i].is_empty) {
66 			WL_AP_BIGDATA_LOG(("idx %d "MACDBG" dis %d empty %d\n",
67 				i, MAC2STRDBG((char *)&ap_sta_data[i].mac),
68 				ap_sta_data[i].disconnected, ap_sta_data[i].is_empty));
69 			WL_AP_BIGDATA_LOG(("mode %d nss %d chanspec %d rssi %d "
70 						"rate %d reason_code %d\n\n",
71 						ap_sta_data[i].mode_80211,
72 						ap_sta_data[i].nss, ap_sta_data[i].chanspec,
73 						ap_sta_data[i].rssi, ap_sta_data[i].rate,
74 						ap_sta_data[i].reason_code));
75 		}
76 	}
77 }
78 
79 static inline void
copy_ap_stadata(wl_ap_sta_data_t * dest,wl_ap_sta_data_t * src)80 copy_ap_stadata(wl_ap_sta_data_t *dest, wl_ap_sta_data_t *src)
81 {
82 	memcpy(dest, src, sizeof(wl_ap_sta_data_t));
83 	dest->is_empty = FALSE;
84 	dest->disconnected = FALSE;
85 	dest->reason_code = 0;
86 }
87 
88 static void
get_copy_ptr_stadata(struct ether_addr * sta_mac,wl_ap_sta_data_t * sta_data,uint32 * sta_list_cnt,void ** data)89 get_copy_ptr_stadata(struct ether_addr *sta_mac, wl_ap_sta_data_t *sta_data,
90 		uint32 *sta_list_cnt, void **data)
91 {
92 	int i;
93 	int discon_idx = -1;
94 	int empty_idx = -1;
95 
96 	if  (!sta_mac || !sta_data || !sta_list_cnt ||!data) {
97 		WL_ERR(("sta_mac=%p sta_data=%p sta_lit_cnt=%p data=%p\n",
98 			sta_mac, sta_data, sta_list_cnt, data));
99 		return;
100 	}
101 
102 	/* Find already existed sta */
103 	for (i = 0; i < MAX_STA_INFO_AP_CNT; i++) {
104 		if (!memcmp((char*)sta_mac, (char*)&sta_data[i].mac, ETHER_ADDR_LEN)) {
105 			WL_AP_BIGDATA_LOG(("found existed "
106 						"STA idx %d "MACDBG"\n",
107 						i, MAC2STRDBG((char *)sta_mac)));
108 			*data = (wl_ap_sta_data_t *)&sta_data[i];
109 			return;
110 		}
111 
112 		if (sta_data[i].disconnected && (discon_idx == -1)) {
113 			discon_idx = i;
114 		}
115 
116 		if (sta_data[i].is_empty && (empty_idx == -1)) {
117 			empty_idx = i;
118 		}
119 	}
120 
121 	/* Buf is max */
122 	if (*sta_list_cnt >= MAX_STA_INFO_AP_CNT) {
123 		if (discon_idx != -1) {
124 			WL_AP_BIGDATA_LOG(("delete disconnected "
125 						"idx %d "MACDBG"\n",
126 						discon_idx, MAC2STRDBG((char *)sta_mac)));
127 			*data = (wl_ap_sta_data_t *)&sta_data[discon_idx];
128 			return;
129 		}
130 	}
131 
132 	/* Buf is not max */
133 	if (empty_idx != -1) {
134 		(*sta_list_cnt)++;
135 		WL_AP_BIGDATA_LOG(("empty idx %d \n", empty_idx));
136 		*data = (wl_ap_sta_data_t *)&sta_data[empty_idx];
137 		return;
138 	}
139 }
140 
141 static void
wg_rate_dot11mode(uint32 * rate,uint8 * channel,uint32 * mode_80211)142 wg_rate_dot11mode(uint32 *rate, uint8 *channel, uint32 *mode_80211)
143 {
144 	if (*rate <= DOT11_11B_MAX_RATE) {
145 		/* 11b maximum rate is 11Mbps. 11b mode */
146 		*mode_80211 = BIGDATA_DOT11_11B_MODE;
147 	} else {
148 		/* It's not HT Capable case. */
149 		if (*channel > DOT11_2GHZ_MAX_CH_NUM) {
150 			*mode_80211 = BIGDATA_DOT11_11A_MODE; /* 11a mode */
151 		} else {
152 			*mode_80211 = BIGDATA_DOT11_11G_MODE; /* 11g mode */
153 		}
154 	}
155 }
156 
157 static void
wg_ht_mimo_ant(uint32 * nss,wl_rateset_args_t * rateset)158 wg_ht_mimo_ant(uint32 *nss, wl_rateset_args_t *rateset)
159 {
160 	int i;
161 
162 	*nss = 0;
163 	for (i = 0; i < MAX_STREAMS_SUPPORTED; i++) {
164 		int8 bitmap = DOT11_HT_MCS_RATE_MASK;
165 		if (i == MAX_STREAMS_SUPPORTED-1) {
166 			bitmap = DOT11_RATE_MASK;
167 		}
168 		if (rateset->mcs[i] & bitmap) {
169 			(*nss)++;
170 		}
171 	}
172 }
173 
174 static void
wg_vht_mimo_ant(uint32 * nss,wl_rateset_args_t * rateset)175 wg_vht_mimo_ant(uint32 *nss, wl_rateset_args_t *rateset)
176 {
177 	int i;
178 	uint32 mcs_code;
179 
180 	*nss = 0;
181 
182 	for (i = 1; i <= VHT_CAP_MCS_MAP_NSS_MAX; i++) {
183 		mcs_code = VHT_MCS_MAP_TO_MCS_CODE(rateset->vht_mcs[i - 1]);
184 		if (mcs_code != VHT_CAP_MCS_MAP_NONE) {
185 			(*nss)++;
186 		}
187 	}
188 }
189 
190 #if defined(WL11AX)
191 static void
wg_he_mimo_ant(uint32 * nss,uint16 * mcsset)192 wg_he_mimo_ant(uint32 *nss, uint16 *mcsset)
193 {
194 	int i;
195 
196 	*nss = 0;
197 
198 	for (i = 0; i <= HE_MCS_MAP_NSS_MAX; i++) {
199 		if (mcsset[i]) {
200 			(*nss)++;
201 		}
202 	}
203 }
204 #endif /* WL11AX */
205 
206 static int
wg_parse_ap_stadata(struct net_device * dev,struct ether_addr * sta_mac,wl_ap_sta_data_t * ap_sta_data)207 wg_parse_ap_stadata(struct net_device *dev, struct ether_addr *sta_mac,
208 		wl_ap_sta_data_t *ap_sta_data)
209 {
210 	sta_info_v4_t *sta_v4 = NULL;
211 	sta_info_v5_t *sta_v5 = NULL;
212 	wl_rateset_args_t *rateset_adv;
213 	int ret = BCME_OK;
214 	char* ioctl_buf = NULL;
215 #if defined(WL11AX)
216 	struct wl_rateset_args_v2 *rateset_adv_v2;
217 #endif
218 
219 	ioctl_buf = (char*)kmalloc(WLC_IOCTL_MEDLEN, GFP_KERNEL);
220 	if (ioctl_buf == NULL) {
221 		WL_ERR(("failed to allocated ioctl_buf \n"));
222 		return BCME_ERROR;
223 	}
224 
225 	ret = wldev_iovar_getbuf(dev, "sta_info", (struct ether_addr *)sta_mac,
226 			ETHER_ADDR_LEN, ioctl_buf, WLC_IOCTL_MEDLEN, NULL);
227 
228 	if (ret < 0) {
229 		WL_ERR(("sta_info err value :%d\n", ret));
230 		ret = BCME_ERROR;
231 		goto done;
232 	}
233 
234 	sta_v4 = (sta_info_v4_t *)ioctl_buf;
235 	ap_sta_data->mac = *sta_mac;
236 	ap_sta_data->rssi = 0;
237 	ap_sta_data->mimo = 0;
238 
239 	rateset_adv = &sta_v4->rateset_adv;
240 	ap_sta_data->chanspec = sta_v4->chanspec;
241 
242 	WL_AP_BIGDATA_LOG(("sta_info ver %d\n", sta_v4->ver));
243 
244 	if (sta_v4->ver == WL_STA_VER_5) {
245 		sta_v5 = (sta_info_v5_t *)ioctl_buf;
246 		ap_sta_data->chanspec = sta_v5->chanspec;
247 		rateset_adv = &sta_v5->rateset_adv;
248 	}
249 
250 	ap_sta_data->channel = wf_chspec_ctlchan(ap_sta_data->chanspec);
251 	ap_sta_data->rate =
252 		(sta_v4->rateset.rates[sta_v4->rateset.count - 1] & DOT11_RATE_MASK) / 2;
253 
254 	if (sta_v4->vht_flags) {
255 		ap_sta_data->mode_80211 = BIGDATA_DOT11_11AC_MODE;
256 		wg_vht_mimo_ant(&ap_sta_data->nss, rateset_adv);
257 	} else if (sta_v4->ht_capabilities) {
258 		ap_sta_data->mode_80211 = BIGDATA_DOT11_11N_MODE;
259 		wg_ht_mimo_ant(&ap_sta_data->nss, rateset_adv);
260 	} else {
261 		wg_rate_dot11mode(&ap_sta_data->rate, &ap_sta_data->channel,
262 				&ap_sta_data->mode_80211);
263 	}
264 
265 #if defined(WL11AX)
266 	ret = wldev_iovar_getbuf(dev, "rateset", NULL, 0, ioctl_buf,
267 			sizeof(wl_rateset_args_v2_t), NULL);
268 	if (ret < 0) {
269 		WL_ERR(("get rateset failed = %d\n", ret));
270 	} else {
271 		rateset_adv_v2 = (wl_rateset_args_v2_t *)ioctl_buf;
272 		WL_AP_BIGDATA_LOG(("rateset ver %d\n", rateset_adv_v2->version));
273 
274 		if (rateset_adv_v2->version == RATESET_ARGS_V2) {
275 			rateset_adv_v2 = (wl_rateset_args_v2_t *)&sta_v4->rateset_adv;
276 			if (sta_v4->ver == WL_STA_VER_5) {
277 				rateset_adv_v2 = (wl_rateset_args_v2_t *)&sta_v5->rateset_adv;
278 			}
279 
280 			if (rateset_adv_v2->he_mcs[0]) {
281 				WL_AP_BIGDATA_LOG(("there is he mcs rate\n"));
282 				ap_sta_data->mode_80211 = BIGDATA_DOT11_11AX_MODE;
283 				wg_he_mimo_ant(&ap_sta_data->nss, &rateset_adv_v2->he_mcs[0]);
284 			}
285 		}
286 	}
287 #endif /* WL11AX */
288 
289 	if (ap_sta_data->nss) {
290 		ap_sta_data->nss = ap_sta_data->nss - 1;
291 	}
292 
293 done:
294 	if (ioctl_buf) {
295 		kfree(ioctl_buf);
296 	}
297 
298 	return ret;
299 }
300 
301 void
wl_gather_ap_stadata(void * handle,void * event_info,u8 event)302 wl_gather_ap_stadata(void *handle, void *event_info, u8 event)
303 {
304 	u32 event_type = 0;
305 	u32 reason = 0;
306 	u32 status = 0;
307 	struct ether_addr sta_mac;
308 	dhd_pub_t *dhdp;
309 
310 	struct net_device *dev = NULL;
311 	struct bcm_cfg80211 *cfg = NULL;
312 	wl_event_msg_t *e;
313 
314 	wl_ap_sta_data_t *sta_data;
315 	wl_ap_sta_data_t temp_sta_data;
316 	void *data = NULL;
317 	int i;
318 	int ret;
319 
320 	ap_sta_wq_data_t *wq_event_data = event_info;
321 	if (!wq_event_data) {
322 		WL_ERR(("wq_event_data is NULL\n"));
323 		return;
324 	}
325 
326 	cfg = (struct bcm_cfg80211 *)wq_event_data->bcm_cfg;
327 	if (!cfg || !cfg->ap_sta_info) {
328 		WL_ERR(("cfg=%p ap_sta_info=%p\n", cfg, (cfg ? cfg->ap_sta_info : NULL)));
329 		if (wq_event_data) {
330 			kfree(wq_event_data);
331 		}
332 		return;
333 	}
334 
335 	mutex_lock(&cfg->ap_sta_info->wq_data_sync);
336 
337 	dhdp = (dhd_pub_t *)cfg->pub;
338 	e = &wq_event_data->e;
339 	dev = (struct net_device *)wq_event_data->ndev;
340 
341 	if (!e || !dev) {
342 		WL_ERR(("e=%p dev=%p\n", e, dev));
343 		goto done;
344 	}
345 
346 	if (!wl_get_drv_status(cfg, AP_CREATED, dev)) {
347 		WL_ERR(("skip to gather data becasue interface is not available\n"));
348 		goto done;
349 	}
350 
351 	sta_data = cfg->ap_sta_info->ap_sta_data;
352 
353 	event_type = ntoh32(e->event_type);
354 	reason = ntoh32(e->reason);
355 	status = ntoh32(e->status);
356 	sta_mac = e->addr;
357 
358 	if (!sta_data) {
359 		WL_ERR(("sta_data is NULL\n"));
360 		goto done;
361 	}
362 
363 	WL_AP_BIGDATA_LOG((""MACDBG" event %d status %d reason %d\n",
364 		MAC2STRDBG((char*)&sta_mac), event_type, status, reason));
365 
366 	if (WLC_E_IS_ASSOC(event_type, reason)) {
367 		ret = wg_parse_ap_stadata(dev, &sta_mac, &temp_sta_data);
368 		if (ret < 0) {
369 			WL_AP_BIGDATA_LOG(("sta_info err value :%d\n", ret));
370 			goto done;
371 		}
372 
373 		if (cfg->ap_sta_info->sta_list_cnt == 0) {
374 			copy_ap_stadata(&sta_data[0], &temp_sta_data);
375 			cfg->ap_sta_info->sta_list_cnt++;
376 			dump_ap_stadata(sta_data);
377 		} else {
378 			get_copy_ptr_stadata(&sta_mac, sta_data,
379 					&cfg->ap_sta_info->sta_list_cnt, &data);
380 			if (data != NULL) {
381 				copy_ap_stadata((wl_ap_sta_data_t *)data, &temp_sta_data);
382 				dump_ap_stadata(sta_data);
383 			}
384 		}
385 	}
386 
387 	if (WLC_E_IS_DEAUTH(event_type)) {
388 		/* Find already existed sta */
389 		for (i = 0; i < MAX_STA_INFO_AP_CNT; i++) {
390 			if (!sta_data[i].is_empty &&
391 				!memcmp((char*)&sta_mac, (char*)&sta_data[i].mac, ETHER_ADDR_LEN)) {
392 				WL_AP_BIGDATA_LOG(("found disconnected "
393 							"STA idx %d "MACDBG"\n",
394 							i, MAC2STRDBG((char *)&sta_mac)));
395 				sta_data[i].is_empty = FALSE;
396 				sta_data[i].disconnected = TRUE;
397 				sta_data[i].reason_code = reason;
398 				dump_ap_stadata(sta_data);
399 				goto done;
400 			}
401 		}
402 	}
403 
404 done:
405 	if (wq_event_data) {
406 		ASSERT(dhdp->osh);
407 		MFREE(dhdp->osh, wq_event_data, sizeof(ap_sta_wq_data_t));
408 	}
409 	mutex_unlock(&cfg->ap_sta_info->wq_data_sync);
410 }
411 
412 int
wl_attach_ap_stainfo(void * bcm_cfg)413 wl_attach_ap_stainfo(void *bcm_cfg)
414 {
415 	gfp_t kflags;
416 	uint32 alloc_len;
417 	wl_ap_sta_info_t *sta_info;
418 	wl_ap_sta_data_t *sta_data = NULL;
419 	int i;
420 	struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)bcm_cfg;
421 
422 	if (!cfg) {
423 		WL_ERR(("cfg is NULL\n"));
424 		return -EINVAL;
425 	}
426 
427 	kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
428 
429 	alloc_len = sizeof(wl_ap_sta_info_t);
430 	sta_info = (wl_ap_sta_info_t *)kzalloc(alloc_len, kflags);
431 
432 	if (unlikely(!sta_info)) {
433 		WL_ERR(("could not allocate memory for - "
434 					"wl_ap_sta_info_t\n"));
435 		goto fail;
436 	}
437 	cfg->ap_sta_info = sta_info;
438 
439 	alloc_len = sizeof(wl_ap_sta_data_t) * MAX_STA_INFO_AP_CNT;
440 	sta_data = (wl_ap_sta_data_t *)kzalloc(alloc_len, kflags);
441 
442 	if (unlikely(!sta_data)) {
443 		WL_ERR(("could not allocate memory for - "
444 					"wl_ap_sta_data_t\n"));
445 		goto fail;
446 	}
447 
448 	cfg->ap_sta_info->sta_list_cnt = 0;
449 
450 	for (i = 0; i < MAX_STA_INFO_AP_CNT; i++) {
451 		sta_data[i].is_empty = TRUE;
452 		memset(&sta_data[i].mac, 0, ETHER_ADDR_LEN);
453 	}
454 
455 	cfg->ap_sta_info->ap_sta_data = sta_data;
456 
457 	mutex_init(&cfg->ap_sta_info->wq_data_sync);
458 
459 	WL_ERR(("attach success\n"));
460 
461 	return BCME_OK;
462 
463 fail:
464 	if (sta_data) {
465 		kfree(sta_data);
466 		cfg->ap_sta_info->ap_sta_data = NULL;
467 	}
468 
469 	if (sta_info) {
470 		kfree(sta_info);
471 		cfg->ap_sta_info = NULL;
472 	}
473 
474 	return BCME_ERROR;
475 }
476 
477 int
wl_detach_ap_stainfo(void * bcm_cfg)478 wl_detach_ap_stainfo(void *bcm_cfg)
479 {
480 	struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)bcm_cfg;
481 
482 	if (!cfg || !cfg->ap_sta_info) {
483 		WL_ERR(("cfg=%p ap_sta_info=%p\n",
484 			cfg, (cfg ? cfg->ap_sta_info : NULL)));
485 		return -EINVAL;
486 	}
487 
488 	if (cfg->ap_sta_info->ap_sta_data) {
489 		kfree(cfg->ap_sta_info->ap_sta_data);
490 		cfg->ap_sta_info->ap_sta_data = NULL;
491 	}
492 
493 	mutex_destroy(&cfg->ap_sta_info->wq_data_sync);
494 
495 	kfree(cfg->ap_sta_info);
496 	cfg->ap_sta_info = NULL;
497 
498 	WL_ERR(("detach success\n"));
499 
500 	return BCME_OK;
501 }
502 
503 int
wl_ap_stainfo_init(void * bcm_cfg)504 wl_ap_stainfo_init(void *bcm_cfg)
505 {
506 	int i;
507 	wl_ap_sta_data_t *sta_data;
508 	struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)bcm_cfg;
509 
510 	if (!cfg || !cfg->ap_sta_info) {
511 		WL_ERR(("cfg=%p ap_sta_info=%p\n",
512 			cfg, (cfg ? cfg->ap_sta_info : NULL)));
513 		return -EINVAL;
514 	}
515 
516 	sta_data = cfg->ap_sta_info->ap_sta_data;
517 	cfg->ap_sta_info->sta_list_cnt = 0;
518 
519 	if (!sta_data) {
520 		WL_ERR(("ap_sta_data is NULL\n"));
521 		return -EINVAL;
522 	}
523 
524 	for (i = 0; i < MAX_STA_INFO_AP_CNT; i++) {
525 		sta_data[i].is_empty = TRUE;
526 		memset(&sta_data[i].mac, 0, ETHER_ADDR_LEN);
527 	}
528 
529 	return BCME_OK;
530 }
531 
532 int
wl_get_ap_stadata(void * bcm_cfg,struct ether_addr * sta_mac,void ** data)533 wl_get_ap_stadata(void *bcm_cfg, struct ether_addr *sta_mac, void **data)
534 {
535 	int i;
536 	wl_ap_sta_data_t *sta_data;
537 	struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)bcm_cfg;
538 
539 	if (!cfg || !cfg->ap_sta_info) {
540 		WL_ERR(("cfg=%p ap_sta_info=%p\n",
541 			cfg, (cfg ? cfg->ap_sta_info : NULL)));
542 		return -EINVAL;
543 	}
544 
545 	if  (!sta_mac || !data) {
546 		WL_ERR(("sta_mac=%p data=%p\n", sta_mac, data));
547 		return -EINVAL;
548 	}
549 
550 	sta_data = cfg->ap_sta_info->ap_sta_data;
551 
552 	if (!sta_data) {
553 		WL_ERR(("ap_sta_data is NULL\n"));
554 		return -EINVAL;
555 	}
556 
557 	/* Find already existed sta */
558 	for (i = 0; i < MAX_STA_INFO_AP_CNT; i++) {
559 		if (!sta_data[i].is_empty) {
560 			WL_AP_BIGDATA_LOG(("%d " MACDBG " " MACDBG "\n", i,
561 				MAC2STRDBG((char *)sta_mac),
562 				MAC2STRDBG((char*)&sta_data[i].mac)));
563 
564 			if (!memcmp(sta_mac, (char*)&sta_data[i].mac, ETHER_ADDR_LEN)) {
565 				WL_AP_BIGDATA_LOG(("Found STA idx %d " MACDBG "\n",
566 					i, MAC2STRDBG((char *)&sta_mac)));
567 
568 				*data = (wl_ap_sta_data_t*)&sta_data[i];
569 				return BCME_OK;
570 			}
571 		}
572 	}
573 
574 	return BCME_ERROR;
575 }
576