1/*
2 * Driver O/S-independent utility routines
3 *
4 * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * $Id: bcmutils.c 419467 2013-08-21 09:19:48Z $
18 */
19
20#include <bcm_cfg.h>
21#include <typedefs.h>
22#include <bcmdefs.h>
23#if defined(__FreeBSD__) || defined(__NetBSD__)
24#include <sys/param.h>
25#if __NetBSD_Version__ >= 500000003
26#include <sys/stdarg.h>
27#else
28#include <machine/stdarg.h>
29#endif
30#else
31#include <stdarg.h>
32#endif /* NetBSD */
33#ifdef BCMDRIVER
34
35#include <osl.h>
36#include <bcmutils.h>
37#include <siutils.h>
38#include <bcmnvram.h>
39
40#else /* !BCMDRIVER */
41
42#include <stdio.h>
43#include <string.h>
44#include <bcmutils.h>
45
46#if defined(BCMEXTSUP)
47#include <bcm_osl.h>
48#endif
49
50
51#endif /* !BCMDRIVER */
52
53#if defined(_WIN32) || defined(NDIS) || defined(__vxworks) || defined(_CFE_)
54#include <bcmstdlib.h>
55#endif
56#include <bcmendian.h>
57#include <bcmdevs.h>
58#include <proto/ethernet.h>
59#include <proto/vlan.h>
60#include <proto/bcmip.h>
61#include <proto/802.1d.h>
62#include <proto/802.11.h>
63#ifdef BCMPERFSTATS
64#include <bcmperf.h>
65#endif
66#include <proto/bcmipv6.h>
67void *_bcmutils_dummy_fn = NULL;
68
69#ifdef BCMDRIVER
70
71#ifdef WLC_LOW
72/* nvram vars cache */
73static char *nvram_vars = NULL;
74static int vars_len = -1;
75#endif /* WLC_LOW */
76
77int
78BCMATTACHFN(pktpool_init)(osl_t *osh, pktpool_t *pktp, int *pplen, int plen, bool istx)
79{
80	int i, err = BCME_OK;
81	void *p;
82	int pktplen;
83
84	ASSERT(pktp != NULL);
85	ASSERT(osh != NULL);
86	ASSERT(pplen != NULL);
87
88	pktplen = *pplen;
89
90	bzero(pktp, sizeof(pktpool_t));
91	pktp->inited = TRUE;
92	pktp->istx = istx ? TRUE : FALSE;
93	pktp->plen = (uint16)plen;
94	*pplen = 0;
95
96	pktp->maxlen = PKTPOOL_LEN_MAX;
97	if (pktplen > pktp->maxlen)
98		pktplen = pktp->maxlen;
99
100	for (i = 0; i < pktplen; i++) {
101		p = PKTGET(osh, plen, pktp->istx);
102		if (p == NULL) {
103			/* Not able to allocate all requested pkts
104			 * so just return what was actually allocated
105			 * We can add to the pool later
106			 */
107			if (pktp->w == 0)
108				err = BCME_NOMEM;
109
110			goto exit;
111		}
112
113		PKTSETPOOL(osh, p, TRUE, pktp);
114		pktp->q[i] = p;
115		pktp->w++;
116		pktp->len++;
117#ifdef BCMDBG_POOL
118		pktp->dbg_q[pktp->dbg_qlen++].p = p;
119#endif
120	}
121
122exit:
123	*pplen = pktp->w;
124	pktp->len++; /* Add one for end */
125	return err;
126}
127
128int
129BCMATTACHFN(pktpool_deinit)(osl_t *osh, pktpool_t *pktp)
130{
131	int i;
132	int cnt;
133
134	ASSERT(osh != NULL);
135	ASSERT(pktp != NULL);
136
137	cnt = pktp->len;
138	for (i = 0; i < cnt; i++) {
139		if (pktp->q[i] != NULL) {
140			PKTSETPOOL(osh, pktp->q[i], FALSE, NULL);
141			PKTFREE(osh, pktp->q[i], pktp->istx);
142			pktp->q[i] = NULL;
143			pktp->len--;
144		}
145#ifdef BCMDBG_POOL
146		if (pktp->dbg_q[i].p != NULL)
147			pktp->dbg_q[i].p = NULL;
148#endif
149	}
150	pktp->inited = FALSE;
151
152	/* Are there still pending pkts? */
153	ASSERT(pktpool_len(pktp) == 0);
154
155	return 0;
156}
157
158int
159pktpool_fill(osl_t *osh, pktpool_t *pktp, bool minimal)
160{
161	void *p;
162	int err = 0;
163	int len, psize, maxlen;
164
165	ASSERT(pktpool_plen(pktp) != 0);
166
167	maxlen = pktpool_maxlen(pktp);
168	psize = minimal ? (maxlen >> 2) : maxlen;
169	len = pktpool_len(pktp);
170	for (; len < psize; len++) {
171		p = PKTGET(osh, pktpool_plen(pktp), FALSE);
172		if (p == NULL) {
173			err = BCME_NOMEM;
174			break;
175		}
176
177		if (pktpool_add(pktp, p) != BCME_OK) {
178			PKTFREE(osh, p, FALSE);
179			err = BCME_ERROR;
180			break;
181		}
182	}
183
184	return err;
185}
186
187uint16
188pktpool_avail(pktpool_t *pktp)
189{
190	if (pktp->w == pktp->r)
191		return 0;
192
193	return (pktp->w > pktp->r) ? (pktp->w - pktp->r) : ((pktp->len) - (pktp->r - pktp->w));
194}
195
196static void *
197pktpool_deq(pktpool_t *pktp)
198{
199	void *p;
200
201	if (pktp->r == pktp->w)
202		return NULL;
203
204	p = pktp->q[pktp->r];
205	ASSERT(p != NULL);
206
207	pktp->q[pktp->r++] = NULL;
208	pktp->r %= (pktp->len);
209
210	return p;
211}
212
213static void
214pktpool_enq(pktpool_t *pktp, void *p)
215{
216	uint16 next;
217
218	ASSERT(p != NULL);
219
220	next = (pktp->w + 1) % (pktp->len);
221	if (next == pktp->r) {
222	/* Should not happen; otherwise pkt leak */
223		ASSERT(0);
224		return;
225	}
226
227	ASSERT(pktp->q[pktp->w] == NULL);
228
229	pktp->q[pktp->w] = p;
230	pktp->w = next;
231}
232
233int
234BCMATTACHFN(pktpool_avail_register)(pktpool_t *pktp, pktpool_cb_t cb, void *arg)
235{
236	int i;
237
238	ASSERT(cb != NULL);
239
240	i = pktp->cbcnt;
241	if (i == PKTPOOL_CB_MAX)
242		return BCME_ERROR;
243
244	ASSERT(pktp->cbs[i].cb == NULL);
245	pktp->cbs[i].cb = cb;
246	pktp->cbs[i].arg = arg;
247	pktp->cbcnt++;
248
249	return 0;
250}
251
252int
253BCMATTACHFN(pktpool_empty_register)(pktpool_t *pktp, pktpool_cb_t cb, void *arg)
254{
255	int i;
256
257	ASSERT(cb != NULL);
258
259	i = pktp->ecbcnt;
260	if (i == PKTPOOL_CB_MAX)
261		return BCME_ERROR;
262
263	ASSERT(pktp->ecbs[i].cb == NULL);
264	pktp->ecbs[i].cb = cb;
265	pktp->ecbs[i].arg = arg;
266	pktp->ecbcnt++;
267
268	return 0;
269}
270
271static int
272pktpool_empty_notify(pktpool_t *pktp)
273{
274	int i;
275
276	pktp->empty = TRUE;
277	for (i = 0; i < pktp->ecbcnt; i++) {
278		ASSERT(pktp->ecbs[i].cb != NULL);
279		pktp->ecbs[i].cb(pktp, pktp->ecbs[i].arg);
280	}
281	pktp->empty = FALSE;
282
283	return 0;
284}
285
286#ifdef BCMDBG_POOL
287int
288pktpool_dbg_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg)
289{
290	int i;
291
292	ASSERT(cb);
293
294	i = pktp->dbg_cbcnt;
295	if (i == PKTPOOL_CB_MAX)
296		return BCME_ERROR;
297
298	ASSERT(pktp->dbg_cbs[i].cb == NULL);
299	pktp->dbg_cbs[i].cb = cb;
300	pktp->dbg_cbs[i].arg = arg;
301	pktp->dbg_cbcnt++;
302
303	return 0;
304}
305
306int pktpool_dbg_notify(pktpool_t *pktp);
307
308int
309pktpool_dbg_notify(pktpool_t *pktp)
310{
311	int i;
312
313	for (i = 0; i < pktp->dbg_cbcnt; i++) {
314		ASSERT(pktp->dbg_cbs[i].cb);
315		pktp->dbg_cbs[i].cb(pktp, pktp->dbg_cbs[i].arg);
316	}
317
318	return 0;
319}
320
321int
322pktpool_dbg_dump(pktpool_t *pktp)
323{
324	int i;
325
326	printf("pool len=%d maxlen=%d\n",  pktp->dbg_qlen, pktp->maxlen);
327	for (i = 0; i < pktp->dbg_qlen; i++) {
328		ASSERT(pktp->dbg_q[i].p);
329		printf("%d, p: 0x%x dur:%lu us state:%d\n", i,
330			pktp->dbg_q[i].p, pktp->dbg_q[i].dur/100, PKTPOOLSTATE(pktp->dbg_q[i].p));
331	}
332
333	return 0;
334}
335
336int
337pktpool_stats_dump(pktpool_t *pktp, pktpool_stats_t *stats)
338{
339	int i;
340	int state;
341
342	bzero(stats, sizeof(pktpool_stats_t));
343	for (i = 0; i < pktp->dbg_qlen; i++) {
344		ASSERT(pktp->dbg_q[i].p != NULL);
345
346		state = PKTPOOLSTATE(pktp->dbg_q[i].p);
347		switch (state) {
348			case POOL_TXENQ:
349				stats->enq++; break;
350			case POOL_TXDH:
351				stats->txdh++; break;
352			case POOL_TXD11:
353				stats->txd11++; break;
354			case POOL_RXDH:
355				stats->rxdh++; break;
356			case POOL_RXD11:
357				stats->rxd11++; break;
358			case POOL_RXFILL:
359				stats->rxfill++; break;
360			case POOL_IDLE:
361				stats->idle++; break;
362		}
363	}
364
365	return 0;
366}
367
368int
369pktpool_start_trigger(pktpool_t *pktp, void *p)
370{
371	uint32 cycles, i;
372
373	if (!PKTPOOL(NULL, p))
374		return 0;
375
376	OSL_GETCYCLES(cycles);
377
378	for (i = 0; i < pktp->dbg_qlen; i++) {
379		ASSERT(pktp->dbg_q[i].p != NULL);
380
381		if (pktp->dbg_q[i].p == p) {
382			pktp->dbg_q[i].cycles = cycles;
383			break;
384		}
385	}
386
387	return 0;
388}
389
390int pktpool_stop_trigger(pktpool_t *pktp, void *p);
391int
392pktpool_stop_trigger(pktpool_t *pktp, void *p)
393{
394	uint32 cycles, i;
395
396	if (!PKTPOOL(NULL, p))
397		return 0;
398
399	OSL_GETCYCLES(cycles);
400
401	for (i = 0; i < pktp->dbg_qlen; i++) {
402		ASSERT(pktp->dbg_q[i].p != NULL);
403
404		if (pktp->dbg_q[i].p == p) {
405			if (pktp->dbg_q[i].cycles == 0)
406				break;
407
408			if (cycles >= pktp->dbg_q[i].cycles)
409				pktp->dbg_q[i].dur = cycles - pktp->dbg_q[i].cycles;
410			else
411				pktp->dbg_q[i].dur =
412					(((uint32)-1) - pktp->dbg_q[i].cycles) + cycles + 1;
413
414			pktp->dbg_q[i].cycles = 0;
415			break;
416		}
417	}
418
419	return 0;
420}
421#endif /* BCMDBG_POOL */
422
423int
424pktpool_avail_notify_normal(osl_t *osh, pktpool_t *pktp)
425{
426	ASSERT(pktp);
427	pktp->availcb_excl = NULL;
428	return 0;
429}
430
431int
432pktpool_avail_notify_exclusive(osl_t *osh, pktpool_t *pktp, pktpool_cb_t cb)
433{
434	int i;
435
436	ASSERT(pktp);
437	ASSERT(pktp->availcb_excl == NULL);
438	for (i = 0; i < pktp->cbcnt; i++) {
439		if (cb == pktp->cbs[i].cb) {
440			pktp->availcb_excl = &pktp->cbs[i];
441			break;
442		}
443	}
444
445	if (pktp->availcb_excl == NULL)
446		return BCME_ERROR;
447	else
448		return 0;
449}
450
451static int
452pktpool_avail_notify(pktpool_t *pktp)
453{
454	int i, k, idx;
455	int avail;
456
457	ASSERT(pktp);
458	if (pktp->availcb_excl != NULL) {
459		pktp->availcb_excl->cb(pktp, pktp->availcb_excl->arg);
460		return 0;
461	}
462
463	k = pktp->cbcnt - 1;
464	for (i = 0; i < pktp->cbcnt; i++) {
465		avail = pktpool_avail(pktp);
466
467		if (avail) {
468			if (pktp->cbtoggle)
469				idx = i;
470			else
471				idx = k--;
472
473			ASSERT(pktp->cbs[idx].cb != NULL);
474			pktp->cbs[idx].cb(pktp, pktp->cbs[idx].arg);
475		}
476	}
477
478	/* Alternate between filling from head or tail
479	 */
480	pktp->cbtoggle ^= 1;
481
482	return 0;
483}
484
485void *
486pktpool_get(pktpool_t *pktp)
487{
488	void *p;
489
490	p = pktpool_deq(pktp);
491
492	if (p == NULL) {
493		/* Notify and try to reclaim tx pkts */
494		if (pktp->ecbcnt)
495			pktpool_empty_notify(pktp);
496
497		p = pktpool_deq(pktp);
498	}
499
500	return p;
501}
502
503void
504pktpool_free(pktpool_t *pktp, void *p)
505{
506	ASSERT(p != NULL);
507
508#ifdef BCMDBG_POOL
509	/* pktpool_stop_trigger(pktp, p); */
510#endif
511
512	pktpool_enq(pktp, p);
513
514	if (pktp->emptycb_disable)
515		return;
516
517	if (pktp->cbcnt) {
518		if (pktp->empty == FALSE)
519			pktpool_avail_notify(pktp);
520	}
521}
522
523int
524pktpool_add(pktpool_t *pktp, void *p)
525{
526	ASSERT(p != NULL);
527
528	if (pktpool_len(pktp) == pktp->maxlen)
529		return BCME_RANGE;
530
531	ASSERT(pktpool_plen(pktp) == PKTLEN(NULL, p)); /* pkts in pool have same length */
532	PKTSETPOOL(NULL, p, TRUE, pktp);
533
534	pktp->len++;
535	if (pktp->r > pktp->w) {
536		/* Add to tail */
537		ASSERT(pktp->q[pktp->len - 1] == NULL);
538		pktp->q[pktp->len - 1] = p;
539	} else
540		pktpool_enq(pktp, p);
541
542#ifdef BCMDBG_POOL
543	pktp->dbg_q[pktp->dbg_qlen++].p = p;
544#endif
545
546	return 0;
547}
548
549int
550pktpool_setmaxlen(pktpool_t *pktp, uint16 maxlen)
551{
552	if (maxlen > PKTPOOL_LEN_MAX)
553		maxlen = PKTPOOL_LEN_MAX;
554
555	/* if pool is already beyond maxlen, then just cap it
556	 * since we currently do not reduce the pool len
557	 * already allocated
558	 */
559	pktp->maxlen = (pktpool_len(pktp) > maxlen) ? pktpool_len(pktp) : maxlen;
560
561	return pktp->maxlen;
562}
563
564void
565pktpool_emptycb_disable(pktpool_t *pktp, bool disable)
566{
567	ASSERT(pktp);
568
569	pktp->emptycb_disable = disable;
570}
571
572bool
573pktpool_emptycb_disabled(pktpool_t *pktp)
574{
575	ASSERT(pktp);
576	return pktp->emptycb_disable;
577}
578
579/* copy a pkt buffer chain into a buffer */
580uint
581pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf)
582{
583	uint n, ret = 0;
584
585	if (len < 0)
586		len = 4096;	/* "infinite" */
587
588	/* skip 'offset' bytes */
589	for (; p && offset; p = PKTNEXT(osh, p)) {
590		if (offset < (uint)PKTLEN(osh, p))
591			break;
592		offset -= PKTLEN(osh, p);
593	}
594
595	if (!p)
596		return 0;
597
598	/* copy the data */
599	for (; p && len; p = PKTNEXT(osh, p)) {
600		n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len);
601		bcopy(PKTDATA(osh, p) + offset, buf, n);
602		buf += n;
603		len -= n;
604		ret += n;
605		offset = 0;
606	}
607
608	return ret;
609}
610
611/* copy a buffer into a pkt buffer chain */
612uint
613pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf)
614{
615	uint n, ret = 0;
616
617	/* skip 'offset' bytes */
618	for (; p && offset; p = PKTNEXT(osh, p)) {
619		if (offset < (uint)PKTLEN(osh, p))
620			break;
621		offset -= PKTLEN(osh, p);
622	}
623
624	if (!p)
625		return 0;
626
627	/* copy the data */
628	for (; p && len; p = PKTNEXT(osh, p)) {
629		n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len);
630		bcopy(buf, PKTDATA(osh, p) + offset, n);
631		buf += n;
632		len -= n;
633		ret += n;
634		offset = 0;
635	}
636
637	return ret;
638}
639
640#ifdef NOTYET
641/* copy data from one pkt buffer (chain) to another */
642uint
643pkt2pktcopy(osl_t *osh, void *p1, uint offs1, void *p2, uint offs2, int maxlen)
644{
645	uint8 *dp1, *dp2;
646	uint len1, len2, copylen, totallen;
647
648	for (; p1 && offs; p1 = PKTNEXT(osh, p1)) {
649		if (offs1 < (uint)PKTLEN(osh, p1))
650			break;
651		offs1 -= PKTLEN(osh, p1);
652	}
653	for (; p2 && offs; p2 = PKTNEXT(osh, p2)) {
654		if (offs2 < (uint)PKTLEN(osh, p2))
655			break;
656		offs2 -= PKTLEN(osh, p2);
657	}
658
659	/* Heck w/it, only need the above for now */
660}
661#endif /* NOTYET */
662
663
664/* return total length of buffer chain */
665uint BCMFASTPATH
666pkttotlen(osl_t *osh, void *p)
667{
668	uint total;
669	int len;
670
671	total = 0;
672	for (; p; p = PKTNEXT(osh, p)) {
673		len = PKTLEN(osh, p);
674#ifdef MACOSX
675		if (len < 0) {
676			/* Bad packet length, just drop and exit */
677			printf("wl: pkttotlen bad (%p,%d)\n", p, len);
678			break;
679		}
680#endif /* MACOSX */
681		total += len;
682	}
683
684	return (total);
685}
686
687/* return the last buffer of chained pkt */
688void *
689pktlast(osl_t *osh, void *p)
690{
691	for (; PKTNEXT(osh, p); p = PKTNEXT(osh, p))
692		;
693
694	return (p);
695}
696
697/* count segments of a chained packet */
698uint BCMFASTPATH
699pktsegcnt(osl_t *osh, void *p)
700{
701	uint cnt;
702
703	for (cnt = 0; p; p = PKTNEXT(osh, p))
704		cnt++;
705
706	return cnt;
707}
708
709
710/* count segments of a chained packet */
711uint BCMFASTPATH
712pktsegcnt_war(osl_t *osh, void *p)
713{
714	uint cnt;
715	uint8 *pktdata;
716	uint len, remain, align64;
717
718	for (cnt = 0; p; p = PKTNEXT(osh, p)) {
719		cnt++;
720		len = PKTLEN(osh, p);
721		if (len > 128) {
722			pktdata = (uint8 *)PKTDATA(osh, p);	/* starting address of data */
723			/* Check for page boundary straddle (2048B) */
724			if (((uintptr)pktdata & ~0x7ff) != ((uintptr)(pktdata+len) & ~0x7ff))
725				cnt++;
726
727			align64 = (uint)((uintptr)pktdata & 0x3f);	/* aligned to 64B */
728			align64 = (64 - align64) & 0x3f;
729			len -= align64;		/* bytes from aligned 64B to end */
730			/* if aligned to 128B, check for MOD 128 between 1 to 4B */
731			remain = len % 128;
732			if (remain > 0 && remain <= 4)
733				cnt++;		/* add extra seg */
734		}
735	}
736
737	return cnt;
738}
739
740uint8 * BCMFASTPATH
741pktdataoffset(osl_t *osh, void *p,  uint offset)
742{
743	uint total = pkttotlen(osh, p);
744	uint pkt_off = 0, len = 0;
745	uint8 *pdata = (uint8 *) PKTDATA(osh, p);
746
747	if (offset > total)
748		return NULL;
749
750	for (; p; p = PKTNEXT(osh, p)) {
751		pdata = (uint8 *) PKTDATA(osh, p);
752		pkt_off = offset - len;
753		len += PKTLEN(osh, p);
754		if (len > offset)
755			break;
756	}
757	return (uint8*) (pdata+pkt_off);
758}
759
760
761/* given a offset in pdata, find the pkt seg hdr */
762void *
763pktoffset(osl_t *osh, void *p,  uint offset)
764{
765	uint total = pkttotlen(osh, p);
766	uint len = 0;
767
768	if (offset > total)
769		return NULL;
770
771	for (; p; p = PKTNEXT(osh, p)) {
772		len += PKTLEN(osh, p);
773		if (len > offset)
774			break;
775	}
776	return p;
777}
778
779/*
780 * osl multiple-precedence packet queue
781 * hi_prec is always >= the number of the highest non-empty precedence
782 */
783void * BCMFASTPATH
784pktq_penq(struct pktq *pq, int prec, void *p)
785{
786	struct pktq_prec *q;
787
788	ASSERT(prec >= 0 && prec < pq->num_prec);
789	ASSERT(PKTLINK(p) == NULL);         /* queueing chains not allowed */
790
791	ASSERT(!pktq_full(pq));
792	ASSERT(!pktq_pfull(pq, prec));
793
794	q = &pq->q[prec];
795
796	if (q->head)
797		PKTSETLINK(q->tail, p);
798	else
799		q->head = p;
800
801	q->tail = p;
802	q->len++;
803
804	pq->len++;
805
806	if (pq->hi_prec < prec)
807		pq->hi_prec = (uint8)prec;
808
809	return p;
810}
811
812void * BCMFASTPATH
813pktq_penq_head(struct pktq *pq, int prec, void *p)
814{
815	struct pktq_prec *q;
816
817	ASSERT(prec >= 0 && prec < pq->num_prec);
818	ASSERT(PKTLINK(p) == NULL);         /* queueing chains not allowed */
819
820	ASSERT(!pktq_full(pq));
821	ASSERT(!pktq_pfull(pq, prec));
822
823	q = &pq->q[prec];
824
825	if (q->head == NULL)
826		q->tail = p;
827
828	PKTSETLINK(p, q->head);
829	q->head = p;
830	q->len++;
831
832	pq->len++;
833
834	if (pq->hi_prec < prec)
835		pq->hi_prec = (uint8)prec;
836
837	return p;
838}
839
840void * BCMFASTPATH
841pktq_pdeq(struct pktq *pq, int prec)
842{
843	struct pktq_prec *q;
844	void *p;
845
846	ASSERT(prec >= 0 && prec < pq->num_prec);
847
848	q = &pq->q[prec];
849
850	if ((p = q->head) == NULL)
851		return NULL;
852
853	if ((q->head = PKTLINK(p)) == NULL)
854		q->tail = NULL;
855
856	q->len--;
857
858	pq->len--;
859
860	PKTSETLINK(p, NULL);
861
862	return p;
863}
864
865void * BCMFASTPATH
866pktq_pdeq_prev(struct pktq *pq, int prec, void *prev_p)
867{
868	struct pktq_prec *q;
869	void *p;
870
871	ASSERT(prec >= 0 && prec < pq->num_prec);
872
873	q = &pq->q[prec];
874
875	if (prev_p == NULL)
876		return NULL;
877
878	if ((p = PKTLINK(prev_p)) == NULL)
879		return NULL;
880
881	if (q->tail == p)
882		q->tail = prev_p;
883
884	q->len--;
885
886	pq->len--;
887
888	PKTSETLINK(prev_p, PKTLINK(p));
889	PKTSETLINK(p, NULL);
890
891	return p;
892}
893
894void * BCMFASTPATH
895pktq_pdeq_tail(struct pktq *pq, int prec)
896{
897	struct pktq_prec *q;
898	void *p, *prev;
899
900	ASSERT(prec >= 0 && prec < pq->num_prec);
901
902	q = &pq->q[prec];
903
904	if ((p = q->head) == NULL)
905		return NULL;
906
907	for (prev = NULL; p != q->tail; p = PKTLINK(p))
908		prev = p;
909
910	if (prev)
911		PKTSETLINK(prev, NULL);
912	else
913		q->head = NULL;
914
915	q->tail = prev;
916	q->len--;
917
918	pq->len--;
919
920	return p;
921}
922
923void
924pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, ifpkt_cb_t fn, int arg)
925{
926	struct pktq_prec *q;
927	void *p, *prev = NULL;
928
929	q = &pq->q[prec];
930	p = q->head;
931	while (p) {
932		if (fn == NULL || (*fn)(p, arg)) {
933			bool head = (p == q->head);
934			if (head)
935				q->head = PKTLINK(p);
936			else
937				PKTSETLINK(prev, PKTLINK(p));
938			PKTSETLINK(p, NULL);
939			PKTFREE(osh, p, dir);
940			q->len--;
941			pq->len--;
942			p = (head ? q->head : PKTLINK(prev));
943		} else {
944			prev = p;
945			p = PKTLINK(p);
946		}
947	}
948
949	if (q->head == NULL) {
950		ASSERT(q->len == 0);
951		q->tail = NULL;
952	}
953}
954
955bool BCMFASTPATH
956pktq_pdel(struct pktq *pq, void *pktbuf, int prec)
957{
958	struct pktq_prec *q;
959	void *p;
960
961	ASSERT(prec >= 0 && prec < pq->num_prec);
962
963	if (!pktbuf)
964		return FALSE;
965
966	q = &pq->q[prec];
967
968	if (q->head == pktbuf) {
969		if ((q->head = PKTLINK(pktbuf)) == NULL)
970			q->tail = NULL;
971	} else {
972		for (p = q->head; p && PKTLINK(p) != pktbuf; p = PKTLINK(p))
973			;
974		if (p == NULL)
975			return FALSE;
976
977		PKTSETLINK(p, PKTLINK(pktbuf));
978		if (q->tail == pktbuf)
979			q->tail = p;
980	}
981
982	q->len--;
983	pq->len--;
984	PKTSETLINK(pktbuf, NULL);
985	return TRUE;
986}
987
988void
989pktq_init(struct pktq *pq, int num_prec, int max_len)
990{
991	int prec;
992
993	ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC);
994
995	/* pq is variable size; only zero out what's requested */
996	bzero(pq, OFFSETOF(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
997
998	pq->num_prec = (uint16)num_prec;
999
1000	pq->max = (uint16)max_len;
1001
1002	for (prec = 0; prec < num_prec; prec++)
1003		pq->q[prec].max = pq->max;
1004}
1005
1006void
1007pktq_set_max_plen(struct pktq *pq, int prec, int max_len)
1008{
1009	ASSERT(prec >= 0 && prec < pq->num_prec);
1010
1011	if (prec < pq->num_prec)
1012		pq->q[prec].max = (uint16)max_len;
1013}
1014
1015void * BCMFASTPATH
1016pktq_deq(struct pktq *pq, int *prec_out)
1017{
1018	struct pktq_prec *q;
1019	void *p;
1020	int prec;
1021
1022	if (pq->len == 0)
1023		return NULL;
1024
1025	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
1026		pq->hi_prec--;
1027
1028	q = &pq->q[prec];
1029
1030	if ((p = q->head) == NULL)
1031		return NULL;
1032
1033	if ((q->head = PKTLINK(p)) == NULL)
1034		q->tail = NULL;
1035
1036	q->len--;
1037
1038	pq->len--;
1039
1040	if (prec_out)
1041		*prec_out = prec;
1042
1043	PKTSETLINK(p, NULL);
1044
1045	return p;
1046}
1047
1048void * BCMFASTPATH
1049pktq_deq_tail(struct pktq *pq, int *prec_out)
1050{
1051	struct pktq_prec *q;
1052	void *p, *prev;
1053	int prec;
1054
1055	if (pq->len == 0)
1056		return NULL;
1057
1058	for (prec = 0; prec < pq->hi_prec; prec++)
1059		if (pq->q[prec].head)
1060			break;
1061
1062	q = &pq->q[prec];
1063
1064	if ((p = q->head) == NULL)
1065		return NULL;
1066
1067	for (prev = NULL; p != q->tail; p = PKTLINK(p))
1068		prev = p;
1069
1070	if (prev)
1071		PKTSETLINK(prev, NULL);
1072	else
1073		q->head = NULL;
1074
1075	q->tail = prev;
1076	q->len--;
1077
1078	pq->len--;
1079
1080	if (prec_out)
1081		*prec_out = prec;
1082
1083	PKTSETLINK(p, NULL);
1084
1085	return p;
1086}
1087
1088void *
1089pktq_peek(struct pktq *pq, int *prec_out)
1090{
1091	int prec;
1092
1093	if (pq->len == 0)
1094		return NULL;
1095
1096	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
1097		pq->hi_prec--;
1098
1099	if (prec_out)
1100		*prec_out = prec;
1101
1102	return (pq->q[prec].head);
1103}
1104
1105void *
1106pktq_peek_tail(struct pktq *pq, int *prec_out)
1107{
1108	int prec;
1109
1110	if (pq->len == 0)
1111		return NULL;
1112
1113	for (prec = 0; prec < pq->hi_prec; prec++)
1114		if (pq->q[prec].head)
1115			break;
1116
1117	if (prec_out)
1118		*prec_out = prec;
1119
1120	return (pq->q[prec].tail);
1121}
1122
1123void
1124pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg)
1125{
1126	int prec;
1127
1128	/* Optimize flush, if pktq len = 0, just return.
1129	 * pktq len of 0 means pktq's prec q's are all empty.
1130	 */
1131	if (pq->len == 0) {
1132		return;
1133	}
1134
1135	for (prec = 0; prec < pq->num_prec; prec++)
1136		pktq_pflush(osh, pq, prec, dir, fn, arg);
1137	if (fn == NULL)
1138		ASSERT(pq->len == 0);
1139}
1140
1141/* Return sum of lengths of a specific set of precedences */
1142int
1143pktq_mlen(struct pktq *pq, uint prec_bmp)
1144{
1145	int prec, len;
1146
1147	len = 0;
1148
1149	for (prec = 0; prec <= pq->hi_prec; prec++)
1150		if (prec_bmp & (1 << prec))
1151			len += pq->q[prec].len;
1152
1153	return len;
1154}
1155
1156/* Priority peek from a specific set of precedences */
1157void * BCMFASTPATH
1158pktq_mpeek(struct pktq *pq, uint prec_bmp, int *prec_out)
1159{
1160	struct pktq_prec *q;
1161	void *p;
1162	int prec;
1163
1164	if (pq->len == 0)
1165	{
1166		return NULL;
1167	}
1168	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
1169		pq->hi_prec--;
1170
1171	while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)
1172		if (prec-- == 0)
1173			return NULL;
1174
1175	q = &pq->q[prec];
1176
1177	if ((p = q->head) == NULL)
1178		return NULL;
1179
1180	if (prec_out)
1181		*prec_out = prec;
1182
1183	return p;
1184}
1185/* Priority dequeue from a specific set of precedences */
1186void * BCMFASTPATH
1187pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out)
1188{
1189	struct pktq_prec *q;
1190	void *p;
1191	int prec;
1192
1193	if (pq->len == 0)
1194		return NULL;
1195
1196	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
1197		pq->hi_prec--;
1198
1199	while ((pq->q[prec].head == NULL) || ((prec_bmp & (1 << prec)) == 0))
1200		if (prec-- == 0)
1201			return NULL;
1202
1203	q = &pq->q[prec];
1204
1205	if ((p = q->head) == NULL)
1206		return NULL;
1207
1208	if ((q->head = PKTLINK(p)) == NULL)
1209		q->tail = NULL;
1210
1211	q->len--;
1212
1213	if (prec_out)
1214		*prec_out = prec;
1215
1216	pq->len--;
1217
1218	PKTSETLINK(p, NULL);
1219
1220	return p;
1221}
1222
1223#endif /* BCMDRIVER */
1224
1225#if defined(BCMROMBUILD)
1226const unsigned char BCMROMDATA(bcm_ctype)[] = {
1227#else
1228const unsigned char bcm_ctype[] = {
1229#endif
1230
1231	_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,			/* 0-7 */
1232	_BCM_C, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C,
1233	_BCM_C,	/* 8-15 */
1234	_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,			/* 16-23 */
1235	_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,			/* 24-31 */
1236	_BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,		/* 32-39 */
1237	_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,			/* 40-47 */
1238	_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,			/* 48-55 */
1239	_BCM_D,_BCM_D,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,			/* 56-63 */
1240	_BCM_P, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X,
1241	_BCM_U|_BCM_X, _BCM_U, /* 64-71 */
1242	_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,			/* 72-79 */
1243	_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,			/* 80-87 */
1244	_BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,			/* 88-95 */
1245	_BCM_P, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X,
1246	_BCM_L|_BCM_X, _BCM_L, /* 96-103 */
1247	_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 104-111 */
1248	_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 112-119 */
1249	_BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_C, /* 120-127 */
1250	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* 128-143 */
1251	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* 144-159 */
1252	_BCM_S|_BCM_SP, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,
1253	_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,	/* 160-175 */
1254	_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,
1255	_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,	/* 176-191 */
1256	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U,
1257	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U,	/* 192-207 */
1258	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_P, _BCM_U, _BCM_U, _BCM_U,
1259	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_L,	/* 208-223 */
1260	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L,
1261	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L,	/* 224-239 */
1262	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_P, _BCM_L, _BCM_L, _BCM_L,
1263	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L /* 240-255 */
1264};
1265
1266ulong
1267BCMROMFN(bcm_strtoul)(const char *cp, char **endp, uint base)
1268{
1269	ulong result, last_result = 0, value;
1270	bool minus;
1271
1272	minus = FALSE;
1273
1274	while (bcm_isspace(*cp))
1275		cp++;
1276
1277	if (cp[0] == '+')
1278		cp++;
1279	else if (cp[0] == '-') {
1280		minus = TRUE;
1281		cp++;
1282	}
1283
1284	if (base == 0) {
1285		if (cp[0] == '0') {
1286			if ((cp[1] == 'x') || (cp[1] == 'X')) {
1287				base = 16;
1288				cp = &cp[2];
1289			} else {
1290				base = 8;
1291				cp = &cp[1];
1292			}
1293		} else
1294			base = 10;
1295	} else if (base == 16 && (cp[0] == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) {
1296		cp = &cp[2];
1297	}
1298
1299	result = 0;
1300
1301	while (bcm_isxdigit(*cp) &&
1302	       (value = bcm_isdigit(*cp) ? *cp-'0' : bcm_toupper(*cp)-'A'+10) < base) {
1303		result = result*base + value;
1304		/* Detected overflow */
1305		if (result < last_result && !minus)
1306			return (ulong)-1;
1307		last_result = result;
1308		cp++;
1309	}
1310
1311	if (minus)
1312		result = (ulong)(-(long)result);
1313
1314	if (endp)
1315		*endp = DISCARD_QUAL(cp, char);
1316
1317	return (result);
1318}
1319
1320int
1321BCMROMFN(bcm_atoi)(const char *s)
1322{
1323	return (int)bcm_strtoul(s, NULL, 10);
1324}
1325
1326/* return pointer to location of substring 'needle' in 'haystack' */
1327char *
1328BCMROMFN(bcmstrstr)(const char *haystack, const char *needle)
1329{
1330	int len, nlen;
1331	int i;
1332
1333	if ((haystack == NULL) || (needle == NULL))
1334		return DISCARD_QUAL(haystack, char);
1335
1336	nlen = strlen(needle);
1337	len = strlen(haystack) - nlen + 1;
1338
1339	for (i = 0; i < len; i++)
1340		if (memcmp(needle, &haystack[i], nlen) == 0)
1341			return DISCARD_QUAL(&haystack[i], char);
1342	return (NULL);
1343}
1344
1345char *
1346BCMROMFN(bcmstrcat)(char *dest, const char *src)
1347{
1348	char *p;
1349
1350	p = dest + strlen(dest);
1351
1352	while ((*p++ = *src++) != '\0')
1353		;
1354
1355	return (dest);
1356}
1357
1358char *
1359BCMROMFN(bcmstrncat)(char *dest, const char *src, uint size)
1360{
1361	char *endp;
1362	char *p;
1363
1364	p = dest + strlen(dest);
1365	endp = p + size;
1366
1367	while (p != endp && (*p++ = *src++) != '\0')
1368		;
1369
1370	return (dest);
1371}
1372
1373
1374/****************************************************************************
1375* Function:   bcmstrtok
1376*
1377* Purpose:
1378*  Tokenizes a string. This function is conceptually similiar to ANSI C strtok(),
1379*  but allows strToken() to be used by different strings or callers at the same
1380*  time. Each call modifies '*string' by substituting a NULL character for the
1381*  first delimiter that is encountered, and updates 'string' to point to the char
1382*  after the delimiter. Leading delimiters are skipped.
1383*
1384* Parameters:
1385*  string      (mod) Ptr to string ptr, updated by token.
1386*  delimiters  (in)  Set of delimiter characters.
1387*  tokdelim    (out) Character that delimits the returned token. (May
1388*                    be set to NULL if token delimiter is not required).
1389*
1390* Returns:  Pointer to the next token found. NULL when no more tokens are found.
1391*****************************************************************************
1392*/
1393char *
1394bcmstrtok(char **string, const char *delimiters, char *tokdelim)
1395{
1396	unsigned char *str;
1397	unsigned long map[8];
1398	int count;
1399	char *nextoken;
1400
1401	if (tokdelim != NULL) {
1402		/* Prime the token delimiter */
1403		*tokdelim = '\0';
1404	}
1405
1406	/* Clear control map */
1407	for (count = 0; count < 8; count++) {
1408		map[count] = 0;
1409	}
1410
1411	/* Set bits in delimiter table */
1412	do {
1413		map[*delimiters >> 5] |= (1 << (*delimiters & 31));
1414	}
1415	while (*delimiters++);
1416
1417	str = (unsigned char*)*string;
1418
1419	/* Find beginning of token (skip over leading delimiters). Note that
1420	 * there is no token iff this loop sets str to point to the terminal
1421	 * null (*str == '\0')
1422	 */
1423	while (((map[*str >> 5] & (1 << (*str & 31))) && *str) || (*str == ' ')) {
1424		str++;
1425	}
1426
1427	nextoken = (char*)str;
1428
1429	/* Find the end of the token. If it is not the end of the string,
1430	 * put a null there.
1431	 */
1432	for (; *str; str++) {
1433		if (map[*str >> 5] & (1 << (*str & 31))) {
1434			if (tokdelim != NULL) {
1435				*tokdelim = *str;
1436			}
1437
1438			*str++ = '\0';
1439			break;
1440		}
1441	}
1442
1443	*string = (char*)str;
1444
1445	/* Determine if a token has been found. */
1446	if (nextoken == (char *) str) {
1447		return NULL;
1448	}
1449	else {
1450		return nextoken;
1451	}
1452}
1453
1454
1455#define xToLower(C) \
1456	((C >= 'A' && C <= 'Z') ? (char)((int)C - (int)'A' + (int)'a') : C)
1457
1458
1459/****************************************************************************
1460* Function:   bcmstricmp
1461*
1462* Purpose:    Compare to strings case insensitively.
1463*
1464* Parameters: s1 (in) First string to compare.
1465*             s2 (in) Second string to compare.
1466*
1467* Returns:    Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if
1468*             t1 > t2, when ignoring case sensitivity.
1469*****************************************************************************
1470*/
1471int
1472bcmstricmp(const char *s1, const char *s2)
1473{
1474	char dc, sc;
1475
1476	while (*s2 && *s1) {
1477		dc = xToLower(*s1);
1478		sc = xToLower(*s2);
1479		if (dc < sc) return -1;
1480		if (dc > sc) return 1;
1481		s1++;
1482		s2++;
1483	}
1484
1485	if (*s1 && !*s2) return 1;
1486	if (!*s1 && *s2) return -1;
1487	return 0;
1488}
1489
1490
1491/****************************************************************************
1492* Function:   bcmstrnicmp
1493*
1494* Purpose:    Compare to strings case insensitively, upto a max of 'cnt'
1495*             characters.
1496*
1497* Parameters: s1  (in) First string to compare.
1498*             s2  (in) Second string to compare.
1499*             cnt (in) Max characters to compare.
1500*
1501* Returns:    Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if
1502*             t1 > t2, when ignoring case sensitivity.
1503*****************************************************************************
1504*/
1505int
1506bcmstrnicmp(const char* s1, const char* s2, int cnt)
1507{
1508	char dc, sc;
1509
1510	while (*s2 && *s1 && cnt) {
1511		dc = xToLower(*s1);
1512		sc = xToLower(*s2);
1513		if (dc < sc) return -1;
1514		if (dc > sc) return 1;
1515		s1++;
1516		s2++;
1517		cnt--;
1518	}
1519
1520	if (!cnt) return 0;
1521	if (*s1 && !*s2) return 1;
1522	if (!*s1 && *s2) return -1;
1523	return 0;
1524}
1525
1526/* parse a xx:xx:xx:xx:xx:xx format ethernet address */
1527int
1528BCMROMFN(bcm_ether_atoe)(const char *p, struct ether_addr *ea)
1529{
1530	int i = 0;
1531	char *ep;
1532
1533	for (;;) {
1534		ea->octet[i++] = (char) bcm_strtoul(p, &ep, 16);
1535		p = ep;
1536		if (!*p++ || i == 6)
1537			break;
1538	}
1539
1540	return (i == 6);
1541}
1542
1543
1544#if defined(CONFIG_USBRNDIS_RETAIL) || defined(NDIS_MINIPORT_DRIVER)
1545/* registry routine buffer preparation utility functions:
1546 * parameter order is like strncpy, but returns count
1547 * of bytes copied. Minimum bytes copied is null char(1)/wchar(2)
1548 */
1549ulong
1550wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen)
1551{
1552	ulong copyct = 1;
1553	ushort i;
1554
1555	if (abuflen == 0)
1556		return 0;
1557
1558	/* wbuflen is in bytes */
1559	wbuflen /= sizeof(ushort);
1560
1561	for (i = 0; i < wbuflen; ++i) {
1562		if (--abuflen == 0)
1563			break;
1564		*abuf++ = (char) *wbuf++;
1565		++copyct;
1566	}
1567	*abuf = '\0';
1568
1569	return copyct;
1570}
1571#endif /* CONFIG_USBRNDIS_RETAIL || NDIS_MINIPORT_DRIVER */
1572
1573char *
1574bcm_ether_ntoa(const struct ether_addr *ea, char *buf)
1575{
1576	static const char hex[] =
1577	{
1578		'0', '1', '2', '3', '4', '5', '6', '7',
1579		'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
1580	};
1581	const uint8 *octet = ea->octet;
1582	char *p = buf;
1583	int i;
1584
1585	for (i = 0; i < 6; i++, octet++) {
1586		*p++ = hex[(*octet >> 4) & 0xf];
1587		*p++ = hex[*octet & 0xf];
1588		*p++ = ':';
1589	}
1590
1591	*(p-1) = '\0';
1592
1593	return (buf);
1594}
1595
1596char *
1597bcm_ip_ntoa(struct ipv4_addr *ia, char *buf)
1598{
1599	snprintf(buf, 16, "%d.%d.%d.%d",
1600	         ia->addr[0], ia->addr[1], ia->addr[2], ia->addr[3]);
1601	return (buf);
1602}
1603
1604char *
1605bcm_ipv6_ntoa(void *ipv6, char *buf)
1606{
1607	/* Implementing RFC 5952 Sections 4 + 5 */
1608	/* Not thoroughly tested */
1609	uint16 *a = (uint16 *)ipv6;
1610	char *p = buf;
1611	int i, i_max = -1, cnt = 0, cnt_max = 1;
1612	uint8 *a4 = NULL;
1613
1614	for (i = 0; i < IPV6_ADDR_LEN/2; i++) {
1615		if (a[i]) {
1616			if (cnt > cnt_max) {
1617				cnt_max = cnt;
1618				i_max = i - cnt;
1619			}
1620			cnt = 0;
1621		} else
1622			cnt++;
1623	}
1624	if (cnt > cnt_max) {
1625		cnt_max = cnt;
1626		i_max = i - cnt;
1627	}
1628	if (i_max == 0 &&
1629		/* IPv4-translated: ::ffff:0:a.b.c.d */
1630		((cnt_max == 4 && a[4] == 0xffff && a[5] == 0) ||
1631		/* IPv4-mapped: ::ffff:a.b.c.d */
1632		(cnt_max == 5 && a[5] == 0xffff)))
1633		a4 = (uint8*) (a + 6);
1634
1635	for (i = 0; i < IPV6_ADDR_LEN/2; i++) {
1636		if ((uint8*) (a + i) == a4) {
1637			snprintf(p, 16, ":%u.%u.%u.%u", a4[0], a4[1], a4[2], a4[3]);
1638			break;
1639		} else if (i == i_max) {
1640			*p++ = ':';
1641			i += cnt_max - 1;
1642			p[0] = ':';
1643			p[1] = '\0';
1644		} else {
1645			if (i)
1646				*p++ = ':';
1647			p += snprintf(p, 8, "%x", ntoh16(a[i]));
1648		}
1649	}
1650
1651	return buf;
1652}
1653
1654#ifdef BCMDRIVER
1655
1656void
1657bcm_mdelay(uint ms)
1658{
1659	uint i;
1660
1661	for (i = 0; i < ms; i++) {
1662		OSL_DELAY(1000);
1663	}
1664}
1665
1666/*
1667 * Search the name=value vars for a specific one and return its value.
1668 * Returns NULL if not found.
1669 */
1670char *
1671getvar(char *vars, const char *name)
1672{
1673#ifdef _MINOSL_
1674	return NULL;
1675#else
1676	char *s;
1677	int len;
1678
1679	if (!name)
1680		return NULL;
1681
1682	len = strlen(name);
1683	if (len == 0)
1684		return NULL;
1685
1686	/* first look in vars[] */
1687	for (s = vars; s && *s;) {
1688		if ((bcmp(s, name, len) == 0) && (s[len] == '='))
1689			return (&s[len+1]);
1690
1691		while (*s++)
1692			;
1693	}
1694
1695	/* then query nvram */
1696	return (nvram_get(name));
1697#endif	/* defined(_MINOSL_) */
1698}
1699
1700/*
1701 * Search the vars for a specific one and return its value as
1702 * an integer. Returns 0 if not found.
1703 */
1704int
1705getintvar(char *vars, const char *name)
1706{
1707#ifdef	_MINOSL_
1708	return 0;
1709#else
1710	char *val;
1711
1712	if ((val = getvar(vars, name)) == NULL)
1713		return (0);
1714
1715	return (bcm_strtoul(val, NULL, 0));
1716#endif	/* _MINOSL_ */
1717}
1718
1719int
1720getintvararray(char *vars, const char *name, int index)
1721{
1722#ifdef	_MINOSL_
1723	return 0;
1724#else
1725	char *buf, *endp;
1726	int i = 0;
1727	int val = 0;
1728
1729	if ((buf = getvar(vars, name)) == NULL) {
1730		return (0);
1731	}
1732
1733	/* table values are always separated by "," or " " */
1734	while (*buf != '\0') {
1735		val = bcm_strtoul(buf, &endp, 0);
1736		if (i == index) {
1737			return val;
1738		}
1739		buf = endp;
1740		/* delimiter is ',' */
1741		if (*buf == ',')
1742			buf++;
1743		i++;
1744	}
1745	return 0;
1746#endif	/* _MINOSL_ */
1747}
1748
1749int
1750getintvararraysize(char *vars, const char *name)
1751{
1752#ifdef	_MINOSL_
1753	return 0;
1754#else
1755	char *buf, *endp;
1756	int count = 0;
1757	int val = 0;
1758
1759	if ((buf = getvar(vars, name)) == NULL) {
1760		return (0);
1761	}
1762
1763	/* table values are always separated by "," or " " */
1764	while (*buf != '\0') {
1765		val = bcm_strtoul(buf, &endp, 0);
1766		buf = endp;
1767		/* delimiter is ',' */
1768		if (*buf == ',')
1769			buf++;
1770		count++;
1771	}
1772	BCM_REFERENCE(val);
1773	return count;
1774#endif	/* _MINOSL_ */
1775}
1776
1777/* Search for token in comma separated token-string */
1778static int
1779findmatch(const char *string, const char *name)
1780{
1781	uint len;
1782	char *c;
1783
1784	len = strlen(name);
1785	while ((c = strchr(string, ',')) != NULL) {
1786		if (len == (uint)(c - string) && !strncmp(string, name, len))
1787			return 1;
1788		string = c + 1;
1789	}
1790
1791	return (!strcmp(string, name));
1792}
1793
1794/* Return gpio pin number assigned to the named pin
1795 *
1796 * Variable should be in format:
1797 *
1798 *	gpio<N>=pin_name,pin_name
1799 *
1800 * This format allows multiple features to share the gpio with mutual
1801 * understanding.
1802 *
1803 * 'def_pin' is returned if a specific gpio is not defined for the requested functionality
1804 * and if def_pin is not used by others.
1805 */
1806uint
1807getgpiopin(char *vars, char *pin_name, uint def_pin)
1808{
1809	char name[] = "gpioXXXX";
1810	char *val;
1811	uint pin;
1812
1813	/* Go thru all possibilities till a match in pin name */
1814	for (pin = 0; pin < GPIO_NUMPINS; pin ++) {
1815		snprintf(name, sizeof(name), "gpio%d", pin);
1816		val = getvar(vars, name);
1817		if (val && findmatch(val, pin_name))
1818			return pin;
1819	}
1820
1821	if (def_pin != GPIO_PIN_NOTDEFINED) {
1822		/* make sure the default pin is not used by someone else */
1823		snprintf(name, sizeof(name), "gpio%d", def_pin);
1824		if (getvar(vars, name)) {
1825			def_pin =  GPIO_PIN_NOTDEFINED;
1826		}
1827	}
1828	return def_pin;
1829}
1830
1831#if defined(BCMPERFSTATS) || defined(BCMTSTAMPEDLOGS)
1832
1833#define	LOGSIZE	256			/* should be power of 2 to avoid div below */
1834static struct {
1835	uint	cycles;
1836	char	*fmt;
1837	uint	a1;
1838	uint	a2;
1839} logtab[LOGSIZE];
1840
1841/* last entry logged  */
1842static uint logi = 0;
1843/* next entry to read */
1844static uint readi = 0;
1845#endif	/* defined(BCMPERFSTATS) || defined(BCMTSTAMPEDLOGS) */
1846
1847#ifdef BCMPERFSTATS
1848void
1849bcm_perf_enable()
1850{
1851	BCMPERF_ENABLE_INSTRCOUNT();
1852	BCMPERF_ENABLE_ICACHE_MISS();
1853	BCMPERF_ENABLE_ICACHE_HIT();
1854}
1855
1856/* WARNING:  This routine uses OSL_GETCYCLES(), which can give unexpected results on
1857 * modern speed stepping CPUs.  Use bcmtslog() instead in combination with TSF counter.
1858 */
1859void
1860bcmlog(char *fmt, uint a1, uint a2)
1861{
1862	static uint last = 0;
1863	uint cycles, i;
1864	OSL_GETCYCLES(cycles);
1865
1866	i = logi;
1867
1868	logtab[i].cycles = cycles - last;
1869	logtab[i].fmt = fmt;
1870	logtab[i].a1 = a1;
1871	logtab[i].a2 = a2;
1872
1873	logi = (i + 1) % LOGSIZE;
1874	last = cycles;
1875}
1876
1877
1878void
1879bcmstats(char *fmt)
1880{
1881	static uint last = 0;
1882	static uint32 ic_miss = 0;
1883	static uint32 instr_count = 0;
1884	uint32 ic_miss_cur;
1885	uint32 instr_count_cur;
1886	uint cycles, i;
1887
1888	OSL_GETCYCLES(cycles);
1889	BCMPERF_GETICACHE_MISS(ic_miss_cur);
1890	BCMPERF_GETINSTRCOUNT(instr_count_cur);
1891
1892	i = logi;
1893
1894	logtab[i].cycles = cycles - last;
1895	logtab[i].a1 = ic_miss_cur - ic_miss;
1896	logtab[i].a2 = instr_count_cur - instr_count;
1897	logtab[i].fmt = fmt;
1898
1899	logi = (i + 1) % LOGSIZE;
1900
1901	last = cycles;
1902	instr_count = instr_count_cur;
1903	ic_miss = ic_miss_cur;
1904}
1905
1906
1907void
1908bcmdumplog(char *buf, int size)
1909{
1910	char *limit;
1911	int j = 0;
1912	int num;
1913
1914	limit = buf + size - 80;
1915	*buf = '\0';
1916
1917	num = logi - readi;
1918
1919	if (num < 0)
1920		num += LOGSIZE;
1921
1922	/* print in chronological order */
1923
1924	for (j = 0; j < num && (buf < limit); readi = (readi + 1) % LOGSIZE, j++) {
1925		if (logtab[readi].fmt == NULL)
1926		    continue;
1927		buf += snprintf(buf, (limit - buf), "%d\t", logtab[readi].cycles);
1928		buf += snprintf(buf, (limit - buf), logtab[readi].fmt, logtab[readi].a1,
1929		                logtab[readi].a2);
1930		buf += snprintf(buf, (limit - buf), "\n");
1931	}
1932
1933}
1934
1935
1936/*
1937 * Dump one log entry at a time.
1938 * Return index of next entry or -1 when no more .
1939 */
1940int
1941bcmdumplogent(char *buf, uint i)
1942{
1943	bool hit;
1944
1945	/*
1946	 * If buf is NULL, return the starting index,
1947	 * interpreting i as the indicator of last 'i' entries to dump.
1948	 */
1949	if (buf == NULL) {
1950		i = ((i > 0) && (i < (LOGSIZE - 1))) ? i : (LOGSIZE - 1);
1951		return ((logi - i) % LOGSIZE);
1952	}
1953
1954	*buf = '\0';
1955
1956	ASSERT(i < LOGSIZE);
1957
1958	if (i == logi)
1959		return (-1);
1960
1961	hit = FALSE;
1962	for (; (i != logi) && !hit; i = (i + 1) % LOGSIZE) {
1963		if (logtab[i].fmt == NULL)
1964			continue;
1965		buf += sprintf(buf, "%d: %d\t", i, logtab[i].cycles);
1966		buf += sprintf(buf, logtab[i].fmt, logtab[i].a1, logtab[i].a2);
1967		buf += sprintf(buf, "\n");
1968		hit = TRUE;
1969	}
1970
1971	return (i);
1972}
1973
1974#endif	/* BCMPERFSTATS */
1975
1976#if defined(BCMTSTAMPEDLOGS)
1977/* Store a TSF timestamp and a log line in the log buffer */
1978void
1979bcmtslog(uint32 tstamp, char *fmt, uint a1, uint a2)
1980{
1981	uint i = logi;
1982	bool use_delta = FALSE;
1983	static uint32 last = 0;	/* used only when use_delta is true */
1984
1985	logtab[i].cycles = tstamp;
1986	if (use_delta)
1987		logtab[i].cycles -= last;
1988
1989	logtab[i].fmt = fmt;
1990	logtab[i].a1 = a1;
1991	logtab[i].a2 = a2;
1992
1993	if (use_delta)
1994		last = tstamp;
1995	logi = (i + 1) % LOGSIZE;
1996}
1997
1998/* Print out a microsecond timestamp as "sec.ms.us " */
1999void
2000bcmprinttstamp(uint32 ticks)
2001{
2002	uint us, ms, sec;
2003
2004	us = (ticks % TSF_TICKS_PER_MS) * 1000 / TSF_TICKS_PER_MS;
2005	ms = ticks / TSF_TICKS_PER_MS;
2006	sec = ms / 1000;
2007	ms -= sec * 1000;
2008	printf("%04u.%03u.%03u ", sec, ms, us);
2009}
2010
2011/* Print out the log buffer with timestamps */
2012void
2013bcmprinttslogs(void)
2014{
2015	int j = 0;
2016	int num;
2017
2018	num = logi - readi;
2019	if (num < 0)
2020		num += LOGSIZE;
2021
2022	/* Format and print the log entries directly in chronological order */
2023	for (j = 0; j < num; readi = (readi + 1) % LOGSIZE, j++) {
2024		if (logtab[readi].fmt == NULL)
2025		    continue;
2026		bcmprinttstamp(logtab[readi].cycles);
2027		printf(logtab[readi].fmt, logtab[readi].a1, logtab[readi].a2);
2028		printf("\n");
2029	}
2030}
2031
2032void
2033bcmdumptslog(char *buf, int size)
2034{
2035	char *limit;
2036	int j = 0;
2037	int num;
2038	uint us, ms, sec;
2039
2040	limit = buf + size - 80;
2041	*buf = '\0';
2042
2043	num = logi - readi;
2044
2045	if (num < 0)
2046		num += LOGSIZE;
2047
2048	/* print in chronological order */
2049	for (j = 0; j < num && (buf < limit); readi = (readi + 1) % LOGSIZE, j++) {
2050		if (logtab[readi].fmt == NULL)
2051			continue;
2052		us = (logtab[readi].cycles % TSF_TICKS_PER_MS) * 1000 / TSF_TICKS_PER_MS;
2053		ms = logtab[readi].cycles / TSF_TICKS_PER_MS;
2054		sec = ms / 1000;
2055		ms -= sec * 1000;
2056
2057		buf += snprintf(buf, (limit - buf), "%04u.%03u.%03u ", sec, ms, us);
2058		/*      buf += snprintf(buf, (limit - buf), "%d\t", logtab[readi].cycles); */
2059		buf += snprintf(buf, (limit - buf), logtab[readi].fmt, logtab[readi].a1,
2060		logtab[readi].a2);
2061		buf += snprintf(buf, (limit - buf), "\n");
2062	}
2063}
2064
2065#endif	/* BCMTSTAMPEDLOGS */
2066
2067#if defined(BCMDBG) || defined(DHD_DEBUG)
2068/* pretty hex print a pkt buffer chain */
2069void
2070prpkt(const char *msg, osl_t *osh, void *p0)
2071{
2072	void *p;
2073
2074	if (msg && (msg[0] != '\0'))
2075		printf("%s:\n", msg);
2076
2077	for (p = p0; p; p = PKTNEXT(osh, p))
2078		prhex(NULL, PKTDATA(osh, p), PKTLEN(osh, p));
2079}
2080#endif	/* BCMDBG || DHD_DEBUG */
2081
2082/* Takes an Ethernet frame and sets out-of-bound PKTPRIO.
2083 * Also updates the inplace vlan tag if requested.
2084 * For debugging, it returns an indication of what it did.
2085 */
2086uint BCMFASTPATH
2087pktsetprio(void *pkt, bool update_vtag)
2088{
2089	struct ether_header *eh;
2090	struct ethervlan_header *evh;
2091	uint8 *pktdata;
2092	int priority = 0;
2093	int rc = 0;
2094
2095	pktdata = (uint8 *)PKTDATA(NULL, pkt);
2096	ASSERT(ISALIGNED((uintptr)pktdata, sizeof(uint16)));
2097
2098	eh = (struct ether_header *) pktdata;
2099
2100	if (eh->ether_type == hton16(ETHER_TYPE_8021Q)) {
2101		uint16 vlan_tag;
2102		int vlan_prio, dscp_prio = 0;
2103
2104		evh = (struct ethervlan_header *)eh;
2105
2106		vlan_tag = ntoh16(evh->vlan_tag);
2107		vlan_prio = (int) (vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK;
2108
2109		if (evh->ether_type == hton16(ETHER_TYPE_IP)) {
2110			uint8 *ip_body = pktdata + sizeof(struct ethervlan_header);
2111			uint8 tos_tc = IP_TOS46(ip_body);
2112			dscp_prio = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);
2113		}
2114
2115		/* DSCP priority gets precedence over 802.1P (vlan tag) */
2116		if (dscp_prio != 0) {
2117			priority = dscp_prio;
2118			rc |= PKTPRIO_VDSCP;
2119		} else {
2120			priority = vlan_prio;
2121			rc |= PKTPRIO_VLAN;
2122		}
2123		/*
2124		 * If the DSCP priority is not the same as the VLAN priority,
2125		 * then overwrite the priority field in the vlan tag, with the
2126		 * DSCP priority value. This is required for Linux APs because
2127		 * the VLAN driver on Linux, overwrites the skb->priority field
2128		 * with the priority value in the vlan tag
2129		 */
2130		if (update_vtag && (priority != vlan_prio)) {
2131			vlan_tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT);
2132			vlan_tag |= (uint16)priority << VLAN_PRI_SHIFT;
2133			evh->vlan_tag = hton16(vlan_tag);
2134			rc |= PKTPRIO_UPD;
2135		}
2136	} else if (eh->ether_type == hton16(ETHER_TYPE_IP)) {
2137		uint8 *ip_body = pktdata + sizeof(struct ether_header);
2138		uint8 tos_tc = IP_TOS46(ip_body);
2139		priority = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);
2140		rc |= PKTPRIO_DSCP;
2141	}
2142
2143	ASSERT(priority >= 0 && priority <= MAXPRIO);
2144	PKTSETPRIO(pkt, priority);
2145	return (rc | priority);
2146}
2147
2148#ifndef BCM_BOOTLOADER
2149
2150static char bcm_undeferrstr[32];
2151static const char *const bcmerrorstrtable[] = BCMERRSTRINGTABLE;
2152
2153/* Convert the error codes into related error strings  */
2154const char *
2155bcmerrorstr(int bcmerror)
2156{
2157	/* check if someone added a bcmerror code but forgot to add errorstring */
2158	ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(bcmerrorstrtable) - 1));
2159
2160	if (bcmerror > 0 || bcmerror < BCME_LAST) {
2161		snprintf(bcm_undeferrstr, sizeof(bcm_undeferrstr), "Undefined error %d", bcmerror);
2162		return bcm_undeferrstr;
2163	}
2164
2165	ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN);
2166
2167	return bcmerrorstrtable[-bcmerror];
2168}
2169
2170#endif /* !BCM_BOOTLOADER */
2171
2172#ifdef WLC_LOW
2173static void
2174BCMINITFN(bcm_nvram_refresh)(char *flash)
2175{
2176	int i;
2177	int ret = 0;
2178
2179	ASSERT(flash != NULL);
2180
2181	/* default "empty" vars cache */
2182	bzero(flash, 2);
2183
2184	if ((ret = nvram_getall(flash, MAX_NVRAM_SPACE)))
2185		return;
2186
2187	/* determine nvram length */
2188	for (i = 0; i < MAX_NVRAM_SPACE; i++) {
2189		if (flash[i] == '\0' && flash[i+1] == '\0')
2190			break;
2191	}
2192
2193	if (i > 1)
2194		vars_len = i + 2;
2195	else
2196		vars_len = 0;
2197}
2198
2199char *
2200bcm_nvram_vars(uint *length)
2201{
2202#ifndef BCMNVRAMR
2203	/* cache may be stale if nvram is read/write */
2204	if (nvram_vars) {
2205		ASSERT(!bcmreclaimed);
2206		bcm_nvram_refresh(nvram_vars);
2207	}
2208#endif
2209	if (length)
2210		*length = vars_len;
2211	return nvram_vars;
2212}
2213
2214/* copy nvram vars into locally-allocated multi-string array */
2215int
2216BCMINITFN(bcm_nvram_cache)(void *sih)
2217{
2218	int ret = 0;
2219	void *osh;
2220	char *flash = NULL;
2221
2222	if (vars_len >= 0) {
2223#ifndef BCMNVRAMR
2224		bcm_nvram_refresh(nvram_vars);
2225#endif
2226		return 0;
2227	}
2228
2229	osh = si_osh((si_t *)sih);
2230
2231	/* allocate memory and read in flash */
2232	if (!(flash = MALLOC(osh, MAX_NVRAM_SPACE))) {
2233		ret = BCME_NOMEM;
2234		goto exit;
2235	}
2236
2237	bcm_nvram_refresh(flash);
2238
2239#ifdef BCMNVRAMR
2240	if (vars_len > 3) {
2241		/* copy into a properly-sized buffer */
2242		if (!(nvram_vars = MALLOC(osh, vars_len))) {
2243			ret = BCME_NOMEM;
2244		} else
2245			bcopy(flash, nvram_vars, vars_len);
2246	}
2247	MFREE(osh, flash, MAX_NVRAM_SPACE);
2248#else
2249	/* cache must be full size of nvram if read/write */
2250	nvram_vars = flash;
2251#endif	/* BCMNVRAMR */
2252
2253exit:
2254	return ret;
2255}
2256#endif /* WLC_LOW */
2257
2258
2259/* iovar table lookup */
2260const bcm_iovar_t*
2261bcm_iovar_lookup(const bcm_iovar_t *table, const char *name)
2262{
2263	const bcm_iovar_t *vi;
2264	const char *lookup_name;
2265
2266	/* skip any ':' delimited option prefixes */
2267	lookup_name = strrchr(name, ':');
2268	if (lookup_name != NULL)
2269		lookup_name++;
2270	else
2271		lookup_name = name;
2272
2273	ASSERT(table != NULL);
2274
2275	for (vi = table; vi->name; vi++) {
2276		if (!strcmp(vi->name, lookup_name))
2277			return vi;
2278	}
2279	/* ran to end of table */
2280
2281	return NULL; /* var name not found */
2282}
2283
2284int
2285bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set)
2286{
2287	int bcmerror = 0;
2288
2289	/* length check on io buf */
2290	switch (vi->type) {
2291	case IOVT_BOOL:
2292	case IOVT_INT8:
2293	case IOVT_INT16:
2294	case IOVT_INT32:
2295	case IOVT_UINT8:
2296	case IOVT_UINT16:
2297	case IOVT_UINT32:
2298		/* all integers are int32 sized args at the ioctl interface */
2299		if (len < (int)sizeof(int)) {
2300			bcmerror = BCME_BUFTOOSHORT;
2301		}
2302		break;
2303
2304	case IOVT_BUFFER:
2305		/* buffer must meet minimum length requirement */
2306		if (len < vi->minlen) {
2307			bcmerror = BCME_BUFTOOSHORT;
2308		}
2309		break;
2310
2311	case IOVT_VOID:
2312		if (!set) {
2313			/* Cannot return nil... */
2314			bcmerror = BCME_UNSUPPORTED;
2315		} else if (len) {
2316			/* Set is an action w/o parameters */
2317			bcmerror = BCME_BUFTOOLONG;
2318		}
2319		break;
2320
2321	default:
2322		/* unknown type for length check in iovar info */
2323		ASSERT(0);
2324		bcmerror = BCME_UNSUPPORTED;
2325	}
2326
2327	return bcmerror;
2328}
2329
2330#endif	/* BCMDRIVER */
2331
2332
2333/*******************************************************************************
2334 * crc8
2335 *
2336 * Computes a crc8 over the input data using the polynomial:
2337 *
2338 *       x^8 + x^7 +x^6 + x^4 + x^2 + 1
2339 *
2340 * The caller provides the initial value (either CRC8_INIT_VALUE
2341 * or the previous returned value) to allow for processing of
2342 * discontiguous blocks of data.  When generating the CRC the
2343 * caller is responsible for complementing the final return value
2344 * and inserting it into the byte stream.  When checking, a final
2345 * return value of CRC8_GOOD_VALUE indicates a valid CRC.
2346 *
2347 * Reference: Dallas Semiconductor Application Note 27
2348 *   Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
2349 *     ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
2350 *     ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
2351 *
2352 * ****************************************************************************
2353 */
2354
2355static const uint8 crc8_table[256] = {
2356    0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
2357    0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
2358    0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
2359    0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
2360    0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
2361    0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
2362    0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
2363    0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
2364    0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
2365    0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
2366    0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
2367    0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
2368    0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
2369    0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
2370    0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
2371    0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
2372    0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
2373    0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
2374    0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
2375    0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
2376    0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
2377    0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
2378    0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
2379    0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
2380    0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
2381    0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
2382    0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
2383    0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
2384    0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
2385    0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
2386    0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
2387    0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F
2388};
2389
2390#define CRC_INNER_LOOP(n, c, x) \
2391	(c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff]
2392
2393uint8
2394BCMROMFN(hndcrc8)(
2395	uint8 *pdata,	/* pointer to array of data to process */
2396	uint  nbytes,	/* number of input data bytes to process */
2397	uint8 crc	/* either CRC8_INIT_VALUE or previous return value */
2398)
2399{
2400	/* hard code the crc loop instead of using CRC_INNER_LOOP macro
2401	 * to avoid the undefined and unnecessary (uint8 >> 8) operation.
2402	 */
2403	while (nbytes-- > 0)
2404		crc = crc8_table[(crc ^ *pdata++) & 0xff];
2405
2406	return crc;
2407}
2408
2409/*******************************************************************************
2410 * crc16
2411 *
2412 * Computes a crc16 over the input data using the polynomial:
2413 *
2414 *       x^16 + x^12 +x^5 + 1
2415 *
2416 * The caller provides the initial value (either CRC16_INIT_VALUE
2417 * or the previous returned value) to allow for processing of
2418 * discontiguous blocks of data.  When generating the CRC the
2419 * caller is responsible for complementing the final return value
2420 * and inserting it into the byte stream.  When checking, a final
2421 * return value of CRC16_GOOD_VALUE indicates a valid CRC.
2422 *
2423 * Reference: Dallas Semiconductor Application Note 27
2424 *   Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
2425 *     ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
2426 *     ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
2427 *
2428 * ****************************************************************************
2429 */
2430
2431static const uint16 crc16_table[256] = {
2432    0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
2433    0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
2434    0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
2435    0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
2436    0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
2437    0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
2438    0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
2439    0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
2440    0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
2441    0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
2442    0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
2443    0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
2444    0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
2445    0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
2446    0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
2447    0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
2448    0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
2449    0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
2450    0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
2451    0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
2452    0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
2453    0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
2454    0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
2455    0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
2456    0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
2457    0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
2458    0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
2459    0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
2460    0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
2461    0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
2462    0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
2463    0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
2464};
2465
2466uint16
2467BCMROMFN(hndcrc16)(
2468    uint8 *pdata,  /* pointer to array of data to process */
2469    uint nbytes, /* number of input data bytes to process */
2470    uint16 crc     /* either CRC16_INIT_VALUE or previous return value */
2471)
2472{
2473	while (nbytes-- > 0)
2474		CRC_INNER_LOOP(16, crc, *pdata++);
2475	return crc;
2476}
2477
2478static const uint32 crc32_table[256] = {
2479    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
2480    0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
2481    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
2482    0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
2483    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
2484    0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
2485    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
2486    0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
2487    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
2488    0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
2489    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
2490    0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
2491    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
2492    0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
2493    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
2494    0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
2495    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
2496    0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
2497    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
2498    0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
2499    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
2500    0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
2501    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
2502    0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
2503    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
2504    0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
2505    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
2506    0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
2507    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
2508    0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
2509    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
2510    0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
2511    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
2512    0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
2513    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
2514    0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
2515    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
2516    0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
2517    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
2518    0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
2519    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
2520    0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
2521    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
2522    0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
2523    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
2524    0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
2525    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
2526    0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
2527    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
2528    0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
2529    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
2530    0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
2531    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
2532    0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
2533    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
2534    0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
2535    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
2536    0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
2537    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
2538    0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
2539    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
2540    0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
2541    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
2542    0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
2543};
2544
2545/*
2546 * crc input is CRC32_INIT_VALUE for a fresh start, or previous return value if
2547 * accumulating over multiple pieces.
2548 */
2549uint32
2550BCMROMFN(hndcrc32)(uint8 *pdata, uint nbytes, uint32 crc)
2551{
2552	uint8 *pend;
2553#ifdef __mips__
2554	uint8 tmp[4];
2555	ulong *tptr = (ulong *)tmp;
2556
2557	if (nbytes > 3) {
2558		/* in case the beginning of the buffer isn't aligned */
2559		pend = (uint8 *)((uint)(pdata + 3) & ~0x3);
2560		nbytes -= (pend - pdata);
2561		while (pdata < pend)
2562			CRC_INNER_LOOP(32, crc, *pdata++);
2563	}
2564
2565	if (nbytes > 3) {
2566		/* handle bulk of data as 32-bit words */
2567		pend = pdata + (nbytes & ~0x3);
2568		while (pdata < pend) {
2569			*tptr = *(ulong *)pdata;
2570			pdata += sizeof(ulong *);
2571			CRC_INNER_LOOP(32, crc, tmp[0]);
2572			CRC_INNER_LOOP(32, crc, tmp[1]);
2573			CRC_INNER_LOOP(32, crc, tmp[2]);
2574			CRC_INNER_LOOP(32, crc, tmp[3]);
2575		}
2576	}
2577
2578	/* 1-3 bytes at end of buffer */
2579	pend = pdata + (nbytes & 0x03);
2580	while (pdata < pend)
2581		CRC_INNER_LOOP(32, crc, *pdata++);
2582#else
2583	pend = pdata + nbytes;
2584	while (pdata < pend)
2585		CRC_INNER_LOOP(32, crc, *pdata++);
2586#endif /* __mips__ */
2587
2588	return crc;
2589}
2590
2591#ifdef notdef
2592#define CLEN 	1499 	/*  CRC Length */
2593#define CBUFSIZ 	(CLEN+4)
2594#define CNBUFS		5 /* # of bufs */
2595
2596void
2597testcrc32(void)
2598{
2599	uint j, k, l;
2600	uint8 *buf;
2601	uint len[CNBUFS];
2602	uint32 crcr;
2603	uint32 crc32tv[CNBUFS] =
2604		{0xd2cb1faa, 0xd385c8fa, 0xf5b4f3f3, 0x55789e20, 0x00343110};
2605
2606	ASSERT((buf = MALLOC(CBUFSIZ*CNBUFS)) != NULL);
2607
2608	/* step through all possible alignments */
2609	for (l = 0; l <= 4; l++) {
2610		for (j = 0; j < CNBUFS; j++) {
2611			len[j] = CLEN;
2612			for (k = 0; k < len[j]; k++)
2613				*(buf + j*CBUFSIZ + (k+l)) = (j+k) & 0xff;
2614		}
2615
2616		for (j = 0; j < CNBUFS; j++) {
2617			crcr = crc32(buf + j*CBUFSIZ + l, len[j], CRC32_INIT_VALUE);
2618			ASSERT(crcr == crc32tv[j]);
2619		}
2620	}
2621
2622	MFREE(buf, CBUFSIZ*CNBUFS);
2623	return;
2624}
2625#endif /* notdef */
2626
2627/*
2628 * Advance from the current 1-byte tag/1-byte length/variable-length value
2629 * triple, to the next, returning a pointer to the next.
2630 * If the current or next TLV is invalid (does not fit in given buffer length),
2631 * NULL is returned.
2632 * *buflen is not modified if the TLV elt parameter is invalid, or is decremented
2633 * by the TLV parameter's length if it is valid.
2634 */
2635bcm_tlv_t *
2636BCMROMFN(bcm_next_tlv)(bcm_tlv_t *elt, int *buflen)
2637{
2638	int len;
2639
2640	/* validate current elt */
2641	if (!bcm_valid_tlv(elt, *buflen))
2642		return NULL;
2643
2644	/* advance to next elt */
2645	len = elt->len;
2646	elt = (bcm_tlv_t*)(elt->data + len);
2647	*buflen -= (TLV_HDR_LEN + len);
2648
2649	/* validate next elt */
2650	if (!bcm_valid_tlv(elt, *buflen))
2651		return NULL;
2652
2653	return elt;
2654}
2655
2656/*
2657 * Traverse a string of 1-byte tag/1-byte length/variable-length value
2658 * triples, returning a pointer to the substring whose first element
2659 * matches tag
2660 */
2661bcm_tlv_t *
2662BCMROMFN(bcm_parse_tlvs)(void *buf, int buflen, uint key)
2663{
2664	bcm_tlv_t *elt;
2665	int totlen;
2666
2667	elt = (bcm_tlv_t*)buf;
2668	totlen = buflen;
2669
2670	/* find tagged parameter */
2671	while (totlen >= TLV_HDR_LEN) {
2672		int len = elt->len;
2673
2674		/* validate remaining totlen */
2675		if ((elt->id == key) &&
2676		    (totlen >= (len + TLV_HDR_LEN)))
2677			return (elt);
2678
2679		elt = (bcm_tlv_t*)((uint8*)elt + (len + TLV_HDR_LEN));
2680		totlen -= (len + TLV_HDR_LEN);
2681	}
2682
2683	return NULL;
2684}
2685
2686/*
2687 * Traverse a string of 1-byte tag/1-byte length/variable-length value
2688 * triples, returning a pointer to the substring whose first element
2689 * matches tag.  Stop parsing when we see an element whose ID is greater
2690 * than the target key.
2691 */
2692bcm_tlv_t *
2693BCMROMFN(bcm_parse_ordered_tlvs)(void *buf, int buflen, uint key)
2694{
2695	bcm_tlv_t *elt;
2696	int totlen;
2697
2698	elt = (bcm_tlv_t*)buf;
2699	totlen = buflen;
2700
2701	/* find tagged parameter */
2702	while (totlen >= TLV_HDR_LEN) {
2703		uint id = elt->id;
2704		int len = elt->len;
2705
2706		/* Punt if we start seeing IDs > than target key */
2707		if (id > key)
2708			return (NULL);
2709
2710		/* validate remaining totlen */
2711		if ((id == key) &&
2712		    (totlen >= (len + TLV_HDR_LEN)))
2713			return (elt);
2714
2715		elt = (bcm_tlv_t*)((uint8*)elt + (len + TLV_HDR_LEN));
2716		totlen -= (len + TLV_HDR_LEN);
2717	}
2718	return NULL;
2719}
2720
2721#if defined(BCMDBG) || defined(BCMDBG_ERR) || defined(WLMSG_PRHDRS) || \
2722	defined(WLMSG_PRPKT) || defined(WLMSG_ASSOC) || defined(DHD_DEBUG)
2723int
2724bcm_format_field(const bcm_bit_desc_ex_t *bd, uint32 flags, char* buf, int len)
2725{
2726	int i, slen = 0;
2727	uint32 bit, mask;
2728	const char *name;
2729	mask = bd->mask;
2730	if (len < 2 || !buf)
2731		return 0;
2732
2733	buf[0] = '\0';
2734
2735	for (i = 0;  (name = bd->bitfield[i].name) != NULL; i++) {
2736		bit = bd->bitfield[i].bit;
2737		if ((flags & mask) == bit) {
2738			if (len > (int)strlen(name)) {
2739				slen = strlen(name);
2740				strncpy(buf, name, slen+1);
2741			}
2742			break;
2743		}
2744	}
2745	return slen;
2746}
2747
2748int
2749bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len)
2750{
2751	int i;
2752	char* p = buf;
2753	char hexstr[16];
2754	int slen = 0, nlen = 0;
2755	uint32 bit;
2756	const char* name;
2757
2758	if (len < 2 || !buf)
2759		return 0;
2760
2761	buf[0] = '\0';
2762
2763	for (i = 0; flags != 0; i++) {
2764		bit = bd[i].bit;
2765		name = bd[i].name;
2766		if (bit == 0 && flags != 0) {
2767			/* print any unnamed bits */
2768			snprintf(hexstr, 16, "0x%X", flags);
2769			name = hexstr;
2770			flags = 0;	/* exit loop */
2771		} else if ((flags & bit) == 0)
2772			continue;
2773		flags &= ~bit;
2774		nlen = strlen(name);
2775		slen += nlen;
2776		/* count btwn flag space */
2777		if (flags != 0)
2778			slen += 1;
2779		/* need NULL char as well */
2780		if (len <= slen)
2781			break;
2782		/* copy NULL char but don't count it */
2783		strncpy(p, name, nlen + 1);
2784		p += nlen;
2785		/* copy btwn flag space and NULL char */
2786		if (flags != 0)
2787			p += snprintf(p, 2, " ");
2788	}
2789
2790	/* indicate the str was too short */
2791	if (flags != 0) {
2792		if (len < 2)
2793			p -= 2 - len;	/* overwrite last char */
2794		p += snprintf(p, 2, ">");
2795	}
2796
2797	return (int)(p - buf);
2798}
2799
2800/* print bytes formatted as hex to a string. return the resulting string length */
2801int
2802bcm_format_hex(char *str, const void *bytes, int len)
2803{
2804	int i;
2805	char *p = str;
2806	const uint8 *src = (const uint8*)bytes;
2807
2808	for (i = 0; i < len; i++) {
2809		p += snprintf(p, 3, "%02X", *src);
2810		src++;
2811	}
2812	return (int)(p - str);
2813}
2814#endif
2815
2816/* pretty hex print a contiguous buffer */
2817void
2818prhex(const char *msg, uchar *buf, uint nbytes)
2819{
2820	char line[128], *p;
2821	int len = sizeof(line);
2822	int nchar;
2823	uint i;
2824
2825	if (msg && (msg[0] != '\0'))
2826		printf("%s:\n", msg);
2827
2828	p = line;
2829	for (i = 0; i < nbytes; i++) {
2830		if (i % 16 == 0) {
2831			nchar = snprintf(p, len, "  %04d: ", i);	/* line prefix */
2832			p += nchar;
2833			len -= nchar;
2834		}
2835		if (len > 0) {
2836			nchar = snprintf(p, len, "%02x ", buf[i]);
2837			p += nchar;
2838			len -= nchar;
2839		}
2840
2841		if (i % 16 == 15) {
2842			printf("%s\n", line);		/* flush line */
2843			p = line;
2844			len = sizeof(line);
2845		}
2846	}
2847
2848	/* flush last partial line */
2849	if (p != line)
2850		printf("%s\n", line);
2851}
2852
2853static const char *crypto_algo_names[] = {
2854	"NONE",
2855	"WEP1",
2856	"TKIP",
2857	"WEP128",
2858	"AES_CCM",
2859	"AES_OCB_MSDU",
2860	"AES_OCB_MPDU",
2861	"NALG"
2862	"UNDEF",
2863	"UNDEF",
2864	"UNDEF",
2865	"UNDEF"
2866};
2867
2868const char *
2869bcm_crypto_algo_name(uint algo)
2870{
2871	return (algo < ARRAYSIZE(crypto_algo_names)) ? crypto_algo_names[algo] : "ERR";
2872}
2873
2874#ifdef BCMDBG
2875void
2876deadbeef(void *p, uint len)
2877{
2878	static uint8 meat[] = { 0xde, 0xad, 0xbe, 0xef };
2879
2880	while (len-- > 0) {
2881		*(uint8*)p = meat[((uintptr)p) & 3];
2882		p = (uint8*)p + 1;
2883	}
2884}
2885#endif /* BCMDBG */
2886
2887char *
2888bcm_chipname(uint chipid, char *buf, uint len)
2889{
2890	const char *fmt;
2891
2892	fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
2893	snprintf(buf, len, fmt, chipid);
2894	return buf;
2895}
2896
2897/* Produce a human-readable string for boardrev */
2898char *
2899bcm_brev_str(uint32 brev, char *buf)
2900{
2901	if (brev < 0x100)
2902		snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);
2903	else
2904		snprintf(buf, 8, "%c%03x", ((brev & 0xf000) == 0x1000) ? 'P' : 'A', brev & 0xfff);
2905
2906	return (buf);
2907}
2908
2909#define BUFSIZE_TODUMP_ATONCE 512 /* Buffer size */
2910
2911/* dump large strings to console */
2912void
2913printbig(char *buf)
2914{
2915	uint len, max_len;
2916	char c;
2917
2918	len = strlen(buf);
2919
2920	max_len = BUFSIZE_TODUMP_ATONCE;
2921
2922	while (len > max_len) {
2923		c = buf[max_len];
2924		buf[max_len] = '\0';
2925		printf("%s", buf);
2926		buf[max_len] = c;
2927
2928		buf += max_len;
2929		len -= max_len;
2930	}
2931	/* print the remaining string */
2932	printf("%s\n", buf);
2933	return;
2934}
2935
2936/* routine to dump fields in a fileddesc structure */
2937uint
2938bcmdumpfields(bcmutl_rdreg_rtn read_rtn, void *arg0, uint arg1, struct fielddesc *fielddesc_array,
2939	char *buf, uint32 bufsize)
2940{
2941	uint  filled_len;
2942	int len;
2943	struct fielddesc *cur_ptr;
2944
2945	filled_len = 0;
2946	cur_ptr = fielddesc_array;
2947
2948	while (bufsize > 1) {
2949		if (cur_ptr->nameandfmt == NULL)
2950			break;
2951		len = snprintf(buf, bufsize, cur_ptr->nameandfmt,
2952		               read_rtn(arg0, arg1, cur_ptr->offset));
2953		/* check for snprintf overflow or error */
2954		if (len < 0 || (uint32)len >= bufsize)
2955			len = bufsize - 1;
2956		buf += len;
2957		bufsize -= len;
2958		filled_len += len;
2959		cur_ptr++;
2960	}
2961	return filled_len;
2962}
2963
2964uint
2965bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)
2966{
2967	uint len;
2968
2969	len = strlen(name) + 1;
2970
2971	if ((len + datalen) > buflen)
2972		return 0;
2973
2974	strncpy(buf, name, buflen);
2975
2976	/* append data onto the end of the name string */
2977	memcpy(&buf[len], data, datalen);
2978	len += datalen;
2979
2980	return len;
2981}
2982
2983/* Quarter dBm units to mW
2984 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
2985 * Table is offset so the last entry is largest mW value that fits in
2986 * a uint16.
2987 */
2988
2989#define QDBM_OFFSET 153		/* Offset for first entry */
2990#define QDBM_TABLE_LEN 40	/* Table size */
2991
2992/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
2993 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
2994 */
2995#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
2996
2997/* Largest mW value that will round down to the last table entry,
2998 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
2999 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
3000 */
3001#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
3002
3003static const uint16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
3004/* qdBm: 	+0 	+1 	+2 	+3 	+4 	+5 	+6 	+7 */
3005/* 153: */      6683,	7079,	7499,	7943,	8414,	8913,	9441,	10000,
3006/* 161: */      10593,	11220,	11885,	12589,	13335,	14125,	14962,	15849,
3007/* 169: */      16788,	17783,	18836,	19953,	21135,	22387,	23714,	25119,
3008/* 177: */      26607,	28184,	29854,	31623,	33497,	35481,	37584,	39811,
3009/* 185: */      42170,	44668,	47315,	50119,	53088,	56234,	59566,	63096
3010};
3011
3012uint16
3013BCMROMFN(bcm_qdbm_to_mw)(uint8 qdbm)
3014{
3015	uint factor = 1;
3016	int idx = qdbm - QDBM_OFFSET;
3017
3018	if (idx >= QDBM_TABLE_LEN) {
3019		/* clamp to max uint16 mW value */
3020		return 0xFFFF;
3021	}
3022
3023	/* scale the qdBm index up to the range of the table 0-40
3024	 * where an offset of 40 qdBm equals a factor of 10 mW.
3025	 */
3026	while (idx < 0) {
3027		idx += 40;
3028		factor *= 10;
3029	}
3030
3031	/* return the mW value scaled down to the correct factor of 10,
3032	 * adding in factor/2 to get proper rounding.
3033	 */
3034	return ((nqdBm_to_mW_map[idx] + factor/2) / factor);
3035}
3036
3037uint8
3038BCMROMFN(bcm_mw_to_qdbm)(uint16 mw)
3039{
3040	uint8 qdbm;
3041	int offset;
3042	uint mw_uint = mw;
3043	uint boundary;
3044
3045	/* handle boundary case */
3046	if (mw_uint <= 1)
3047		return 0;
3048
3049	offset = QDBM_OFFSET;
3050
3051	/* move mw into the range of the table */
3052	while (mw_uint < QDBM_TABLE_LOW_BOUND) {
3053		mw_uint *= 10;
3054		offset -= 40;
3055	}
3056
3057	for (qdbm = 0; qdbm < QDBM_TABLE_LEN-1; qdbm++) {
3058		boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm+1] -
3059		                                    nqdBm_to_mW_map[qdbm])/2;
3060		if (mw_uint < boundary) break;
3061	}
3062
3063	qdbm += (uint8)offset;
3064
3065	return (qdbm);
3066}
3067
3068
3069uint
3070BCMROMFN(bcm_bitcount)(uint8 *bitmap, uint length)
3071{
3072	uint bitcount = 0, i;
3073	uint8 tmp;
3074	for (i = 0; i < length; i++) {
3075		tmp = bitmap[i];
3076		while (tmp) {
3077			bitcount++;
3078			tmp &= (tmp - 1);
3079		}
3080	}
3081	return bitcount;
3082}
3083
3084#ifdef BCMDRIVER
3085
3086/* Initialization of bcmstrbuf structure */
3087void
3088bcm_binit(struct bcmstrbuf *b, char *buf, uint size)
3089{
3090	b->origsize = b->size = size;
3091	b->origbuf = b->buf = buf;
3092}
3093
3094/* Buffer sprintf wrapper to guard against buffer overflow */
3095int
3096bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...)
3097{
3098	va_list ap;
3099	int r;
3100
3101	va_start(ap, fmt);
3102
3103	r = vsnprintf(b->buf, b->size, fmt, ap);
3104
3105	/* Non Ansi C99 compliant returns -1,
3106	 * Ansi compliant return r >= b->size,
3107	 * bcmstdlib returns 0, handle all
3108	 */
3109	/* r == 0 is also the case when strlen(fmt) is zero.
3110	 * typically the case when "" is passed as argument.
3111	 */
3112	if ((r == -1) || (r >= (int)b->size)) {
3113		b->size = 0;
3114	} else {
3115		b->size -= r;
3116		b->buf += r;
3117	}
3118
3119	va_end(ap);
3120
3121	return r;
3122}
3123
3124void
3125bcm_bprhex(struct bcmstrbuf *b, const char *msg, bool newline, uint8 *buf, int len)
3126{
3127	int i;
3128
3129	if (msg != NULL && msg[0] != '\0')
3130		bcm_bprintf(b, "%s", msg);
3131	for (i = 0; i < len; i ++)
3132		bcm_bprintf(b, "%02X", buf[i]);
3133	if (newline)
3134		bcm_bprintf(b, "\n");
3135}
3136
3137void
3138bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount)
3139{
3140	int i;
3141
3142	for (i = 0; i < num_bytes; i++) {
3143		num[i] += amount;
3144		if (num[i] >= amount)
3145			break;
3146		amount = 1;
3147	}
3148}
3149
3150int
3151bcm_cmp_bytes(const uchar *arg1, const uchar *arg2, uint8 nbytes)
3152{
3153	int i;
3154
3155	for (i = nbytes - 1; i >= 0; i--) {
3156		if (arg1[i] != arg2[i])
3157			return (arg1[i] - arg2[i]);
3158	}
3159	return 0;
3160}
3161
3162void
3163bcm_print_bytes(const char *name, const uchar *data, int len)
3164{
3165	int i;
3166	int per_line = 0;
3167
3168	printf("%s: %d \n", name ? name : "", len);
3169	for (i = 0; i < len; i++) {
3170		printf("%02x ", *data++);
3171		per_line++;
3172		if (per_line == 16) {
3173			per_line = 0;
3174			printf("\n");
3175		}
3176	}
3177	printf("\n");
3178}
3179#if defined(WLTINYDUMP) || defined(BCMDBG) || defined(WLMSG_INFORM) || \
3180	defined(WLMSG_ASSOC) || defined(WLMSG_PRPKT) || defined(WLMSG_WSEC)
3181#define SSID_FMT_BUF_LEN	((4 * DOT11_MAX_SSID_LEN) + 1)
3182
3183int
3184bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len)
3185{
3186	uint i, c;
3187	char *p = buf;
3188	char *endp = buf + SSID_FMT_BUF_LEN;
3189
3190	if (ssid_len > DOT11_MAX_SSID_LEN) ssid_len = DOT11_MAX_SSID_LEN;
3191
3192	for (i = 0; i < ssid_len; i++) {
3193		c = (uint)ssid[i];
3194		if (c == '\\') {
3195			*p++ = '\\';
3196			*p++ = '\\';
3197		} else if (bcm_isprint((uchar)c)) {
3198			*p++ = (char)c;
3199		} else {
3200			p += snprintf(p, (endp - p), "\\x%02X", c);
3201		}
3202	}
3203	*p = '\0';
3204	ASSERT(p < endp);
3205
3206	return (int)(p - buf);
3207}
3208#endif /* WLTINYDUMP || BCMDBG || WLMSG_INFORM || WLMSG_ASSOC || WLMSG_PRPKT */
3209
3210#endif /* BCMDRIVER */
3211
3212/*
3213 * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file and ending in a NUL.
3214 * also accepts nvram files which are already in the format of <var1>=<value>\0\<var2>=<value2>\0
3215 * Removes carriage returns, empty lines, comment lines, and converts newlines to NULs.
3216 * Shortens buffer as needed and pads with NULs.  End of buffer is marked by two NULs.
3217*/
3218
3219unsigned int
3220process_nvram_vars(char *varbuf, unsigned int len)
3221{
3222	char *dp;
3223	bool findNewline;
3224	int column;
3225	unsigned int buf_len, n;
3226	unsigned int pad = 0;
3227
3228	dp = varbuf;
3229
3230	findNewline = FALSE;
3231	column = 0;
3232
3233	for (n = 0; n < len; n++) {
3234		if (varbuf[n] == '\r')
3235			continue;
3236		if (findNewline && varbuf[n] != '\n')
3237			continue;
3238		findNewline = FALSE;
3239		if (varbuf[n] == '#') {
3240			findNewline = TRUE;
3241			continue;
3242		}
3243		if (varbuf[n] == '\n') {
3244			if (column == 0)
3245				continue;
3246			*dp++ = 0;
3247			column = 0;
3248			continue;
3249		}
3250		*dp++ = varbuf[n];
3251		column++;
3252	}
3253	buf_len = (unsigned int)(dp - varbuf);
3254	if (buf_len % 4) {
3255		pad = 4 - buf_len % 4;
3256		if (pad && (buf_len + pad <= len)) {
3257			buf_len += pad;
3258		}
3259	}
3260
3261	while (dp < varbuf + n)
3262		*dp++ = 0;
3263
3264	return buf_len;
3265}
3266
3267/* calculate a * b + c */
3268void
3269bcm_uint64_multiple_add(uint32* r_high, uint32* r_low, uint32 a, uint32 b, uint32 c)
3270{
3271#define FORMALIZE(var) {cc += (var & 0x80000000) ? 1 : 0; var &= 0x7fffffff;}
3272	uint32 r1, r0;
3273	uint32 a1, a0, b1, b0, t, cc = 0;
3274
3275	a1 = a >> 16;
3276	a0 = a & 0xffff;
3277	b1 = b >> 16;
3278	b0 = b & 0xffff;
3279
3280	r0 = a0 * b0;
3281	FORMALIZE(r0);
3282
3283	t = (a1 * b0) << 16;
3284	FORMALIZE(t);
3285
3286	r0 += t;
3287	FORMALIZE(r0);
3288
3289	t = (a0 * b1) << 16;
3290	FORMALIZE(t);
3291
3292	r0 += t;
3293	FORMALIZE(r0);
3294
3295	FORMALIZE(c);
3296
3297	r0 += c;
3298	FORMALIZE(r0);
3299
3300	r0 |= (cc % 2) ? 0x80000000 : 0;
3301	r1 = a1 * b1 + ((a1 * b0) >> 16) + ((b1 * a0) >> 16) + (cc / 2);
3302
3303	*r_high = r1;
3304	*r_low = r0;
3305}
3306
3307/* calculate a / b */
3308void
3309bcm_uint64_divide(uint32* r, uint32 a_high, uint32 a_low, uint32 b)
3310{
3311	uint32 a1 = a_high, a0 = a_low, r0 = 0;
3312
3313	if (b < 2)
3314		return;
3315
3316	while (a1 != 0) {
3317		r0 += (0xffffffff / b) * a1;
3318		bcm_uint64_multiple_add(&a1, &a0, ((0xffffffff % b) + 1) % b, a1, a0);
3319	}
3320
3321	r0 += a0 / b;
3322	*r = r0;
3323}
3324
3325/* calculate a >> b; and returns only lower 32 bits */
3326void
3327bcm_uint64_right_shift(uint32* r, uint32 a_high, uint32 a_low, uint32 b)
3328{
3329	uint32 a1 = a_high, a0 = a_low, r0 = 0;
3330
3331	if (b == 0) {
3332		r0 = a_low;
3333		*r = r0;
3334		return;
3335	}
3336
3337	if (b < 32) {
3338		a0 = a0 >> b;
3339		a1 = a1 & ((1 << b) - 1);
3340		a1 = a1 << (32 - b);
3341		r0 = a0 | a1;
3342		*r = r0;
3343		return;
3344	} else {
3345		r0 = a1 >> (b - 32);
3346		*r = r0;
3347		return;
3348	}
3349
3350}
3351