1/*
2 * Driver O/S-independent utility routines
3 *
4 * Copyright 2007, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
11 * $Id: bcmutils.c,v 1.1.1.1 2008/10/15 03:31:34 james26_jang Exp $
12 */
13
14#include <typedefs.h>
15#include <bcmdefs.h>
16#include <stdarg.h>
17#include <bcmutils.h>
18#ifdef BCMDRIVER
19#include <osl.h>
20#include <sbutils.h>
21#include <bcmnvram.h>
22#else
23#include <stdio.h>
24#include <string.h>
25#endif /* BCMDRIVER */
26#if defined(_WIN32) || defined(NDIS) || defined(__vxworks) || \
27	defined(_CFE_) || defined(EFI)
28/* debatable */
29#include <bcmstdlib.h>
30#endif
31#include <bcmendian.h>
32#include <bcmdevs.h>
33#include <proto/ethernet.h>
34#include <proto/vlan.h>
35#include <proto/bcmip.h>
36#include <proto/bcmtcp.h>
37#include <proto/802.1d.h>
38
39#ifdef BCMPERFSTATS
40#include <bcmperf.h>
41#endif
42
43#ifdef BCMDRIVER
44/* nvram vars cache */
45static char *nvram_vars = NULL;
46static int vars_len = -1;
47
48/* copy a pkt buffer chain into a buffer */
49uint
50pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf)
51{
52	uint n, ret = 0;
53
54	if (len < 0)
55		len = 4096;	/* "infinite" */
56
57	/* skip 'offset' bytes */
58	for (; p && offset; p = PKTNEXT(osh, p)) {
59		if (offset < (uint)PKTLEN(osh, p))
60			break;
61		offset -= PKTLEN(osh, p);
62	}
63
64	if (!p)
65		return 0;
66
67	/* copy the data */
68	for (; p && len; p = PKTNEXT(osh, p)) {
69		n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len);
70		bcopy(PKTDATA(osh, p) + offset, buf, n);
71		buf += n;
72		len -= n;
73		ret += n;
74		offset = 0;
75	}
76
77	return ret;
78}
79
80/* return total length of buffer chain */
81uint
82pkttotlen(osl_t *osh, void *p)
83{
84	uint total;
85
86	total = 0;
87	for (; p; p = PKTNEXT(osh, p))
88		total += PKTLEN(osh, p);
89	return (total);
90}
91
92/* return the last buffer of chained pkt */
93void *
94pktlast(osl_t *osh, void *p)
95{
96	for (; PKTNEXT(osh, p); p = PKTNEXT(osh, p))
97		;
98
99	return (p);
100}
101
102
103/*
104 * osl multiple-precedence packet queue
105 * hi_prec is always >= the number of the highest non-empty precedence
106 */
107void *
108pktq_penq(struct pktq *pq, int prec, void *p)
109{
110	struct pktq_prec *q;
111
112	ASSERT(prec >= 0 && prec < pq->num_prec);
113	ASSERT(PKTLINK(p) == NULL);         /* queueing chains not allowed */
114
115	ASSERT(!pktq_full(pq));
116	ASSERT(!pktq_pfull(pq, prec));
117
118	q = &pq->q[prec];
119
120	if (q->head)
121		PKTSETLINK(q->tail, p);
122	else
123		q->head = p;
124
125	q->tail = p;
126	q->len++;
127
128	pq->len++;
129
130	if (pq->hi_prec < prec)
131		pq->hi_prec = (uint8)prec;
132
133	return p;
134}
135
136void *
137pktq_penq_head(struct pktq *pq, int prec, void *p)
138{
139	struct pktq_prec *q;
140
141	ASSERT(prec >= 0 && prec < pq->num_prec);
142	ASSERT(PKTLINK(p) == NULL);         /* queueing chains not allowed */
143
144	ASSERT(!pktq_full(pq));
145	ASSERT(!pktq_pfull(pq, prec));
146
147	q = &pq->q[prec];
148
149	if (q->head == NULL)
150		q->tail = p;
151
152	PKTSETLINK(p, q->head);
153	q->head = p;
154	q->len++;
155
156	pq->len++;
157
158	if (pq->hi_prec < prec)
159		pq->hi_prec = (uint8)prec;
160
161	return p;
162}
163
164void *
165pktq_pdeq(struct pktq *pq, int prec)
166{
167	struct pktq_prec *q;
168	void *p;
169
170	ASSERT(prec >= 0 && prec < pq->num_prec);
171
172	q = &pq->q[prec];
173
174	if ((p = q->head) == NULL)
175		return NULL;
176
177	if ((q->head = PKTLINK(p)) == NULL)
178		q->tail = NULL;
179
180	q->len--;
181
182	pq->len--;
183
184	PKTSETLINK(p, NULL);
185
186	return p;
187}
188
189void *
190pktq_pdeq_tail(struct pktq *pq, int prec)
191{
192	struct pktq_prec *q;
193	void *p, *prev;
194
195	ASSERT(prec >= 0 && prec < pq->num_prec);
196
197	q = &pq->q[prec];
198
199	if ((p = q->head) == NULL)
200		return NULL;
201
202	for (prev = NULL; p != q->tail; p = PKTLINK(p))
203		prev = p;
204
205	if (prev)
206		PKTSETLINK(prev, NULL);
207	else
208		q->head = NULL;
209
210	q->tail = prev;
211	q->len--;
212
213	pq->len--;
214
215	return p;
216}
217
218void
219pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir)
220{
221	struct pktq_prec *q;
222	void *p;
223
224	q = &pq->q[prec];
225	p = q->head;
226	while (p) {
227		q->head = PKTLINK(p);
228		PKTSETLINK(p, NULL);
229		PKTFREE(osh, p, dir);
230		q->len--;
231		pq->len--;
232		p = q->head;
233	}
234	ASSERT(q->len == 0);
235	q->tail = NULL;
236}
237
238bool
239pktq_pdel(struct pktq *pq, void *pktbuf, int prec)
240{
241	struct pktq_prec *q;
242	void *p;
243
244	ASSERT(prec >= 0 && prec < pq->num_prec);
245
246	if (!pktbuf)
247		return FALSE;
248
249	q = &pq->q[prec];
250
251	if (q->head == pktbuf) {
252		if ((q->head = PKTLINK(pktbuf)) == NULL)
253			q->tail = NULL;
254	} else {
255		for (p = q->head; p && PKTLINK(p) != pktbuf; p = PKTLINK(p))
256			;
257		if (p == NULL)
258			return FALSE;
259
260		PKTSETLINK(p, PKTLINK(pktbuf));
261		if (q->tail == pktbuf)
262			q->tail = p;
263	}
264
265	q->len--;
266	pq->len--;
267	PKTSETLINK(pktbuf, NULL);
268	return TRUE;
269}
270
271void
272pktq_init(struct pktq *pq, int num_prec, int max_len)
273{
274	int prec;
275
276	ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC);
277
278	/* pq is variable size; only zero out what's requested */
279	bzero(pq, OFFSETOF(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
280
281	pq->num_prec = (uint16)num_prec;
282
283	pq->max = (uint16)max_len;
284
285	for (prec = 0; prec < num_prec; prec++)
286		pq->q[prec].max = pq->max;
287}
288
289int
290pktq_setmax(struct pktq *pq, int max_len)
291{
292	int prec;
293
294	if (!max_len)
295		return pq->max;
296
297	pq->max = (uint16)max_len;
298	for (prec = 0; prec < pq->num_prec; prec++)
299		pq->q[prec].max = pq->max;
300
301	return pq->max;
302}
303
304void *
305pktq_deq(struct pktq *pq, int *prec_out)
306{
307	struct pktq_prec *q;
308	void *p;
309	int prec;
310
311	if (pq->len == 0)
312		return NULL;
313
314	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
315		pq->hi_prec--;
316
317	q = &pq->q[prec];
318
319	if ((p = q->head) == NULL)
320		return NULL;
321
322	if ((q->head = PKTLINK(p)) == NULL)
323		q->tail = NULL;
324
325	q->len--;
326
327	pq->len--;
328
329	if (prec_out)
330		*prec_out = prec;
331
332	PKTSETLINK(p, NULL);
333
334	return p;
335}
336
337void *
338pktq_deq_tail(struct pktq *pq, int *prec_out)
339{
340	struct pktq_prec *q;
341	void *p, *prev;
342	int prec;
343
344	if (pq->len == 0)
345		return NULL;
346
347	for (prec = 0; prec < pq->hi_prec; prec++)
348		if (pq->q[prec].head)
349			break;
350
351	q = &pq->q[prec];
352
353	if ((p = q->head) == NULL)
354		return NULL;
355
356	for (prev = NULL; p != q->tail; p = PKTLINK(p))
357		prev = p;
358
359	if (prev)
360		PKTSETLINK(prev, NULL);
361	else
362		q->head = NULL;
363
364	q->tail = prev;
365	q->len--;
366
367	pq->len--;
368
369	if (prec_out)
370		*prec_out = prec;
371
372	PKTSETLINK(p, NULL);
373
374	return p;
375}
376
377void *
378pktq_peek(struct pktq *pq, int *prec_out)
379{
380	int prec;
381
382	if (pq->len == 0)
383		return NULL;
384
385	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
386		pq->hi_prec--;
387
388	if (prec_out)
389		*prec_out = prec;
390
391	return (pq->q[prec].head);
392}
393
394void *
395pktq_peek_tail(struct pktq *pq, int *prec_out)
396{
397	int prec;
398
399	if (pq->len == 0)
400		return NULL;
401
402	for (prec = 0; prec < pq->hi_prec; prec++)
403		if (pq->q[prec].head)
404			break;
405
406	if (prec_out)
407		*prec_out = prec;
408
409	return (pq->q[prec].tail);
410}
411
412void
413pktq_flush(osl_t *osh, struct pktq *pq, bool dir)
414{
415	int prec;
416	for (prec = 0; prec < pq->num_prec; prec++)
417		pktq_pflush(osh, pq, prec, dir);
418	ASSERT(pq->len == 0);
419}
420
421/* Return sum of lengths of a specific set of precedences */
422int
423pktq_mlen(struct pktq *pq, uint prec_bmp)
424{
425	int prec, len;
426
427	len = 0;
428
429	for (prec = 0; prec <= pq->hi_prec; prec++)
430		if (prec_bmp & (1 << prec))
431			len += pq->q[prec].len;
432
433	return len;
434}
435
436/* Priority dequeue from a specific set of precedences */
437void *
438pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out)
439{
440	struct pktq_prec *q;
441	void *p;
442	int prec;
443
444	if (pq->len == 0)
445		return NULL;
446
447	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
448		pq->hi_prec--;
449
450	while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)
451		if (prec-- == 0)
452			return NULL;
453
454	q = &pq->q[prec];
455
456	if ((p = q->head) == NULL)
457		return NULL;
458
459	if ((q->head = PKTLINK(p)) == NULL)
460		q->tail = NULL;
461
462	q->len--;
463
464	if (prec_out)
465		*prec_out = prec;
466
467	pq->len--;
468
469	PKTSETLINK(p, NULL);
470
471	return p;
472}
473#endif /* BCMDRIVER */
474
475
476const unsigned char bcm_ctype[] = {
477	_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,			/* 0-7 */
478	_BCM_C, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C,
479	_BCM_C,	/* 8-15 */
480	_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,			/* 16-23 */
481	_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,			/* 24-31 */
482	_BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,		/* 32-39 */
483	_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,			/* 40-47 */
484	_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,			/* 48-55 */
485	_BCM_D,_BCM_D,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,			/* 56-63 */
486	_BCM_P, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X,
487	_BCM_U|_BCM_X, _BCM_U, /* 64-71 */
488	_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,			/* 72-79 */
489	_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,			/* 80-87 */
490	_BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,			/* 88-95 */
491	_BCM_P, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X,
492	_BCM_L|_BCM_X, _BCM_L, /* 96-103 */
493	_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 104-111 */
494	_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 112-119 */
495	_BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_C, /* 120-127 */
496	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* 128-143 */
497	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* 144-159 */
498	_BCM_S|_BCM_SP, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,
499	_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,	/* 160-175 */
500	_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,
501	_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,	/* 176-191 */
502	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U,
503	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U,	/* 192-207 */
504	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_P, _BCM_U, _BCM_U, _BCM_U,
505	_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_L,	/* 208-223 */
506	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L,
507	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L,	/* 224-239 */
508	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_P, _BCM_L, _BCM_L, _BCM_L,
509	_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L /* 240-255 */
510};
511
512ulong
513BCMROMFN(bcm_strtoul)(char *cp, char **endp, uint base)
514{
515	ulong result, value;
516	bool minus;
517
518	minus = FALSE;
519
520	while (bcm_isspace(*cp))
521		cp++;
522
523	if (cp[0] == '+')
524		cp++;
525	else if (cp[0] == '-') {
526		minus = TRUE;
527		cp++;
528	}
529
530	if (base == 0) {
531		if (cp[0] == '0') {
532			if ((cp[1] == 'x') || (cp[1] == 'X')) {
533				base = 16;
534				cp = &cp[2];
535			} else {
536				base = 8;
537				cp = &cp[1];
538			}
539		} else
540			base = 10;
541	} else if (base == 16 && (cp[0] == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) {
542		cp = &cp[2];
543	}
544
545	result = 0;
546
547	while (bcm_isxdigit(*cp) &&
548	       (value = bcm_isdigit(*cp) ? *cp-'0' : bcm_toupper(*cp)-'A'+10) < base) {
549		result = result*base + value;
550		cp++;
551	}
552
553	if (minus)
554		result = (ulong)(result * -1);
555
556	if (endp)
557		*endp = (char *)cp;
558
559	return (result);
560}
561
562int
563BCMROMFN(bcm_atoi)(char *s)
564{
565	return (int)bcm_strtoul(s, NULL, 10);
566}
567
568/* return pointer to location of substring 'needle' in 'haystack' */
569char*
570BCMROMFN(bcmstrstr)(char *haystack, char *needle)
571{
572	int len, nlen;
573	int i;
574
575	if ((haystack == NULL) || (needle == NULL))
576		return (haystack);
577
578	nlen = strlen(needle);
579	len = strlen(haystack) - nlen + 1;
580
581	for (i = 0; i < len; i++)
582		if (memcmp(needle, &haystack[i], nlen) == 0)
583			return (&haystack[i]);
584	return (NULL);
585}
586
587char*
588BCMROMFN(bcmstrcat)(char *dest, const char *src)
589{
590	strcpy(&dest[strlen(dest)], src);
591	return (dest);
592}
593
594char*
595BCMROMFN(bcmstrncat)(char *dest, const char *src, uint size)
596{
597	char *endp;
598	char *p;
599
600	p = dest + strlen(dest);
601	endp = p + size;
602
603	while (p != endp && (*p++ = *src++) != '\0')
604		;
605
606	return (dest);
607}
608
609/* parse a xx:xx:xx:xx:xx:xx format ethernet address */
610int
611BCMROMFN(bcm_ether_atoe)(char *p, struct ether_addr *ea)
612{
613	int i = 0;
614
615	for (;;) {
616		ea->octet[i++] = (char) bcm_strtoul(p, &p, 16);
617		if (!*p++ || i == 6)
618			break;
619	}
620
621	return (i == 6);
622}
623
624#if defined(CONFIG_USBRNDIS_RETAIL) || defined(NDIS_MINIPORT_DRIVER)
625/* registry routine buffer preparation utility functions:
626 * parameter order is like strncpy, but returns count
627 * of bytes copied. Minimum bytes copied is null char(1)/wchar(2)
628 */
629ulong
630wchar2ascii(
631	char *abuf,
632	ushort *wbuf,
633	ushort wbuflen,
634	ulong abuflen
635)
636{
637	ulong copyct = 1;
638	ushort i;
639
640	if (abuflen == 0)
641		return 0;
642
643	/* wbuflen is in bytes */
644	wbuflen /= sizeof(ushort);
645
646	for (i = 0; i < wbuflen; ++i) {
647		if (--abuflen == 0)
648			break;
649		*abuf++ = (char) *wbuf++;
650		++copyct;
651	}
652	*abuf = '\0';
653
654	return copyct;
655}
656#endif /* CONFIG_USBRNDIS_RETAIL || NDIS_MINIPORT_DRIVER */
657
658char *
659bcm_ether_ntoa(struct ether_addr *ea, char *buf)
660{
661	snprintf(buf, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
662		ea->octet[0]&0xff, ea->octet[1]&0xff, ea->octet[2]&0xff,
663		ea->octet[3]&0xff, ea->octet[4]&0xff, ea->octet[5]&0xff);
664	return (buf);
665}
666
667char *
668bcm_ip_ntoa(struct ipv4_addr *ia, char *buf)
669{
670	snprintf(buf, 16, "%d.%d.%d.%d",
671	         ia->addr[0], ia->addr[1], ia->addr[2], ia->addr[3]);
672	return (buf);
673}
674
675#ifdef BCMDRIVER
676
677void
678bcm_mdelay(uint ms)
679{
680	uint i;
681
682	for (i = 0; i < ms; i++) {
683		OSL_DELAY(1000);
684	}
685}
686
687/*
688 * Search the name=value vars for a specific one and return its value.
689 * Returns NULL if not found.
690 */
691char *
692getvar(char *vars, const char *name)
693{
694#ifdef	_MINOSL_
695	return NULL;
696#else
697	char *s;
698	int len;
699
700	if (!name)
701		return NULL;
702
703	len = strlen(name);
704	if (len == 0)
705		return NULL;
706
707	/* first look in vars[] */
708	for (s = vars; s && *s;) {
709		/* CSTYLED */
710		if ((bcmp(s, name, len) == 0) && (s[len] == '='))
711			return (&s[len+1]);
712
713		while (*s++)
714			;
715	}
716
717	/* then query nvram */
718	return (nvram_get(name));
719#endif	/* _MINOSL_ */
720}
721
722/*
723 * Search the vars for a specific one and return its value as
724 * an integer. Returns 0 if not found.
725 */
726int
727getintvar(char *vars, const char *name)
728{
729#ifdef	_MINOSL_
730	return 0;
731#else
732	char *val;
733
734	if ((val = getvar(vars, name)) == NULL)
735		return (0);
736
737	return (bcm_strtoul(val, NULL, 0));
738#endif	/* _MINOSL_ */
739}
740
741
742/* Search for token in comma separated token-string */
743static int
744findmatch(char *string, char *name)
745{
746	uint len;
747	char *c;
748
749	len = strlen(name);
750	/* CSTYLED */
751	while ((c = strchr(string, ',')) != NULL) {
752		if (len == (uint)(c - string) && !strncmp(string, name, len))
753			return 1;
754		string = c + 1;
755	}
756
757	return (!strcmp(string, name));
758}
759
760/* Return gpio pin number assigned to the named pin
761 *
762 * Variable should be in format:
763 *
764 *	gpio<N>=pin_name,pin_name
765 *
766 * This format allows multiple features to share the gpio with mutual
767 * understanding.
768 *
769 * 'def_pin' is returned if a specific gpio is not defined for the requested functionality
770 * and if def_pin is not used by others.
771 */
772uint
773getgpiopin(char *vars, char *pin_name, uint def_pin)
774{
775	char name[] = "gpioXXXX";
776	char *val;
777	uint pin;
778
779	/* Go thru all possibilities till a match in pin name */
780	for (pin = 0; pin < GPIO_NUMPINS; pin ++) {
781		snprintf(name, sizeof(name), "gpio%d", pin);
782		val = getvar(vars, name);
783		if (val && findmatch(val, pin_name))
784			return pin;
785	}
786
787	if (def_pin != GPIO_PIN_NOTDEFINED) {
788		/* make sure the default pin is not used by someone else */
789		snprintf(name, sizeof(name), "gpio%d", def_pin);
790		if (getvar(vars, name)) {
791			def_pin =  GPIO_PIN_NOTDEFINED;
792		}
793	}
794
795	return def_pin;
796}
797
798#ifdef BCMPERFSTATS
799
800#define	LOGSIZE	256			/* should be power of 2 to avoid div below */
801static struct {
802	uint	cycles;
803	char	*fmt;
804	uint	a1;
805	uint	a2;
806} logtab[LOGSIZE];
807
808/* last entry logged  */
809static uint logi = 0;
810/* next entry to read */
811static uint readi = 0;
812
813void
814bcm_perf_enable()
815{
816	BCMPERF_ENABLE_INSTRCOUNT();
817	BCMPERF_ENABLE_ICACHE_MISS();
818	BCMPERF_ENABLE_ICACHE_HIT();
819}
820
821void
822bcmlog(char *fmt, uint a1, uint a2)
823{
824	static uint last = 0;
825	uint cycles, i;
826	OSL_GETCYCLES(cycles);
827
828	i = logi;
829
830	logtab[i].cycles = cycles - last;
831	logtab[i].fmt = fmt;
832	logtab[i].a1 = a1;
833	logtab[i].a2 = a2;
834
835	logi = (i + 1) % LOGSIZE;
836	last = cycles;
837}
838
839
840void
841bcmstats(char *fmt)
842{
843	static uint last = 0;
844	static uint32 ic_miss = 0;
845	static uint32 instr_count = 0;
846	uint32 ic_miss_cur;
847	uint32 instr_count_cur;
848	uint cycles, i;
849
850	OSL_GETCYCLES(cycles);
851	BCMPERF_GETICACHE_MISS(ic_miss_cur);
852	BCMPERF_GETINSTRCOUNT(instr_count_cur);
853
854	i = logi;
855
856	logtab[i].cycles = cycles - last;
857	logtab[i].a1 = ic_miss_cur - ic_miss;
858	logtab[i].a2 = instr_count_cur - instr_count;
859	logtab[i].fmt = fmt;
860
861	logi = (i + 1) % LOGSIZE;
862
863	last = cycles;
864	instr_count = instr_count_cur;
865	ic_miss = ic_miss_cur;
866}
867
868
869void
870bcmdumplog(char *buf, int size)
871{
872	char *limit, *line;
873	int j = 0;
874	int num;
875
876	limit = buf + size - 80;
877	*buf = '\0';
878
879	num = logi - readi;
880
881	if (num < 0)
882		num += LOGSIZE;
883
884	/* print in chronological order */
885
886	for (j = 0; j < num && (buf < limit); readi = (readi + 1) % LOGSIZE, j++) {
887		if (logtab[readi].fmt == NULL)
888		    continue;
889		line = buf;
890		buf += sprintf(buf, "%d\t", logtab[readi].cycles);
891		buf += sprintf(buf, logtab[readi].fmt, logtab[readi].a1, logtab[readi].a2);
892		buf += sprintf(buf, "\n");
893	}
894
895}
896
897
898/*
899 * Dump one log entry at a time.
900 * Return index of next entry or -1 when no more .
901 */
902int
903bcmdumplogent(char *buf, uint i)
904{
905	bool hit;
906
907	/*
908	 * If buf is NULL, return the starting index,
909	 * interpreting i as the indicator of last 'i' entries to dump.
910	 */
911	if (buf == NULL) {
912		i = ((i > 0) && (i < (LOGSIZE - 1))) ? i : (LOGSIZE - 1);
913		return ((logi - i) % LOGSIZE);
914	}
915
916	*buf = '\0';
917
918	ASSERT(i < LOGSIZE);
919
920	if (i == logi)
921		return (-1);
922
923	hit = FALSE;
924	for (; (i != logi) && !hit; i = (i + 1) % LOGSIZE) {
925		if (logtab[i].fmt == NULL)
926			continue;
927		buf += sprintf(buf, "%d: %d\t", i, logtab[i].cycles);
928		buf += sprintf(buf, logtab[i].fmt, logtab[i].a1, logtab[i].a2);
929		buf += sprintf(buf, "\n");
930		hit = TRUE;
931	}
932
933	return (i);
934}
935
936#endif	/* BCMPERFSTATS */
937
938
939/* Takes an Ethernet frame and sets out-of-bound PKTPRIO.
940 * Also updates the inplace vlan tag if requested.
941 * For debugging, it returns an indication of what it did.
942 */
943uint
944pktsetprio(void *pkt, bool update_vtag)
945{
946	struct ether_header *eh;
947	struct ethervlan_header *evh;
948	uint8 *pktdata;
949	int priority = 0;
950	int rc = 0;
951
952	pktdata = (uint8 *) PKTDATA(NULL, pkt);
953	ASSERT(ISALIGNED((uintptr)pktdata, sizeof(uint16)));
954
955	eh = (struct ether_header *) pktdata;
956
957	if (ntoh16(eh->ether_type) == ETHER_TYPE_8021Q) {
958		uint16 vlan_tag;
959		int vlan_prio, dscp_prio = 0;
960
961		evh = (struct ethervlan_header *)eh;
962
963		vlan_tag = ntoh16(evh->vlan_tag);
964		vlan_prio = (int) (vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK;
965
966		if (ntoh16(evh->ether_type) == ETHER_TYPE_IP) {
967			uint8 *ip_body = pktdata + sizeof(struct ethervlan_header);
968			uint8 tos_tc = IP_TOS(ip_body);
969			dscp_prio = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);
970			if ((IP_VER(ip_body) == IP_VER_4) && (IPV4_PROT(ip_body) == IP_PROT_TCP)) {
971				int ip_len;
972				int src_port;
973				bool src_port_exc;
974				uint8 *tcp_hdr;
975
976				ip_len = IPV4_PAYLOAD_LEN(ip_body);
977				tcp_hdr = IPV4_NO_OPTIONS_PAYLOAD(ip_body);
978				src_port = TCP_SRC_PORT(tcp_hdr);
979				src_port_exc = (src_port == 10110) || (src_port == 10120) ||
980					(src_port == 10130) || (src_port == 10140);
981
982				if ((ip_len == 40) && src_port_exc && TCP_IS_ACK(tcp_hdr)) {
983					dscp_prio = 7;
984				}
985			}
986		}
987
988		/* DSCP priority gets precedence over 802.1P (vlan tag) */
989		if (dscp_prio != 0) {
990			priority = dscp_prio;
991			rc |= PKTPRIO_VDSCP;
992		} else {
993			priority = vlan_prio;
994			rc |= PKTPRIO_VLAN;
995		}
996		/*
997		 * If the DSCP priority is not the same as the VLAN priority,
998		 * then overwrite the priority field in the vlan tag, with the
999		 * DSCP priority value. This is required for Linux APs because
1000		 * the VLAN driver on Linux, overwrites the skb->priority field
1001		 * with the priority value in the vlan tag
1002		 */
1003		if (update_vtag && (priority != vlan_prio)) {
1004			vlan_tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT);
1005			vlan_tag |= (uint16)priority << VLAN_PRI_SHIFT;
1006			evh->vlan_tag = hton16(vlan_tag);
1007			rc |= PKTPRIO_UPD;
1008		}
1009	} else if (ntoh16(eh->ether_type) == ETHER_TYPE_IP) {
1010		uint8 *ip_body = pktdata + sizeof(struct ether_header);
1011		uint8 tos_tc = IP_TOS(ip_body);
1012		priority = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);
1013		rc |= PKTPRIO_DSCP;
1014		if ((IP_VER(ip_body) == IP_VER_4) && (IPV4_PROT(ip_body) == IP_PROT_TCP)) {
1015			int ip_len;
1016			int src_port;
1017			bool src_port_exc;
1018			uint8 *tcp_hdr;
1019
1020			ip_len = IPV4_PAYLOAD_LEN(ip_body);
1021			tcp_hdr = IPV4_NO_OPTIONS_PAYLOAD(ip_body);
1022			src_port = TCP_SRC_PORT(tcp_hdr);
1023			src_port_exc = (src_port == 10110) || (src_port == 10120) ||
1024				(src_port == 10130) || (src_port == 10140);
1025
1026			if ((ip_len == 40) && src_port_exc && TCP_IS_ACK(tcp_hdr)) {
1027				priority = 7;
1028			}
1029		}
1030	}
1031
1032	ASSERT(priority >= 0 && priority <= MAXPRIO);
1033	PKTSETPRIO(pkt, priority);
1034	return (rc | priority);
1035}
1036
1037static char bcm_undeferrstr[BCME_STRLEN];
1038
1039static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE;
1040
1041/* Convert the error codes into related error strings  */
1042const char *
1043bcmerrorstr(int bcmerror)
1044{
1045	/* check if someone added a bcmerror code but forgot to add errorstring */
1046	ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(bcmerrorstrtable) - 1));
1047
1048	if (bcmerror > 0 || bcmerror < BCME_LAST) {
1049		snprintf(bcm_undeferrstr, BCME_STRLEN, "Undefined error %d", bcmerror);
1050		return bcm_undeferrstr;
1051	}
1052
1053	ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN);
1054
1055	return bcmerrorstrtable[-bcmerror];
1056}
1057
1058static void
1059BCMINITFN(bcm_nvram_refresh)(char *flash)
1060{
1061	int i;
1062	int ret = 0;
1063
1064	ASSERT(flash);
1065
1066	/* default "empty" vars cache */
1067	bzero(flash, 2);
1068
1069	if ((ret = nvram_getall(flash, NVRAM_SPACE)))
1070		return;
1071
1072	/* determine nvram length */
1073	for (i = 0; i < NVRAM_SPACE; i++) {
1074		if (flash[i] == '\0' && flash[i+1] == '\0')
1075			break;
1076	}
1077
1078	if (i > 1)
1079		vars_len = i + 2;
1080	else
1081		vars_len = 0;
1082}
1083
1084char *
1085bcm_nvram_vars(uint *length)
1086{
1087#ifndef BCMNVRAMR
1088	/* cache may be stale if nvram is read/write */
1089	if (nvram_vars) {
1090		ASSERT(!bcmreclaimed);
1091		bcm_nvram_refresh(nvram_vars);
1092	}
1093#endif
1094	if (length)
1095		*length = vars_len;
1096	return nvram_vars;
1097}
1098
1099/* copy nvram vars into locally-allocated multi-string array */
1100int
1101BCMINITFN(bcm_nvram_cache)(void *sbh)
1102{
1103	void *osh;
1104	int ret = 0;
1105	char *flash = NULL;
1106
1107	if (vars_len >= 0) {
1108#ifndef BCMNVRAMR
1109		bcm_nvram_refresh(nvram_vars);
1110#endif
1111		return 0;
1112	}
1113
1114	osh = sb_osh((sb_t *)sbh);
1115
1116	/* allocate memory and read in flash */
1117	if (!(flash = MALLOC(osh, NVRAM_SPACE))) {
1118		ret = BCME_NOMEM;
1119		goto exit;
1120	}
1121
1122	bcm_nvram_refresh(flash);
1123
1124#ifdef BCMNVRAMR
1125	if (vars_len > 3) {
1126		/* copy into a properly-sized buffer */
1127		if (!(nvram_vars = MALLOC(osh, vars_len))) {
1128			ret = BCME_NOMEM;
1129		} else
1130			bcopy(flash, nvram_vars, vars_len);
1131	}
1132	MFREE(osh, flash, NVRAM_SPACE);
1133#else
1134	/* cache must be full size of nvram if read/write */
1135	nvram_vars = flash;
1136#endif	/* BCMNVRAMR */
1137
1138exit:
1139	return ret;
1140}
1141
1142#ifdef BCMDBG_PKT       /* pkt logging for debugging */
1143/* Add a packet to the pktlist */
1144void
1145pktlist_add(pktlist_info_t *pktlist, void *pkt)
1146{
1147	uint i;
1148	ASSERT(pktlist->count < PKTLIST_SIZE);
1149
1150	/* Verify the packet is not already part of the list */
1151	for (i = 0; i < pktlist->count; i++) {
1152		if (pktlist->list[i] == pkt)
1153			ASSERT(0);
1154	}
1155	pktlist->list[pktlist->count] = pkt;
1156	pktlist->count++;
1157	return;
1158}
1159
1160/* Remove a packet from the pktlist */
1161void
1162pktlist_remove(pktlist_info_t *pktlist, void *pkt)
1163{
1164	uint i;
1165	uint num = pktlist->count;
1166
1167	/* find the index where pkt exists */
1168	for (i = 0; i < num; i++)
1169	{
1170		/* check for the existence of pkt in the list */
1171		if (pktlist->list[i] == pkt)
1172		{
1173			/* replace with the last element */
1174			pktlist->list[i] = pktlist->list[num-1];
1175			pktlist->count--;
1176			return;
1177		}
1178	}
1179	ASSERT(0);
1180}
1181
1182/* Dump the pktlist (and the contents of each packet if 'data'
1183 * is set). 'buf' should be large enough
1184 */
1185
1186char *
1187pktlist_dump(pktlist_info_t *pktlist, char *buf)
1188{
1189	char *obuf;
1190	uint i;
1191
1192	obuf = buf;
1193
1194	buf += sprintf(buf, "Packet list dump:\n");
1195
1196	for (i = 0; i < (pktlist->count); i++) {
1197		buf += sprintf(buf, "0x%p\t", pktlist->list[i]);
1198
1199#ifdef NOTDEF     /* Remove this ifdef to print pkttag and pktdata */
1200		if (PKTTAG(pktlist->list[i])) {
1201			/* Print pkttag */
1202			buf += sprintf(buf, "Pkttag(in hex): ");
1203			buf += bcm_format_hex(buf, PKTTAG(pktlist->list[i]), OSL_PKTTAG_SZ);
1204		}
1205		buf += sprintf(buf, "Pktdata(in hex): ");
1206		buf += bcm_format_hex(buf, PKTDATA(NULL, pktlist->list[i]),
1207			PKTLEN(NULL, pktlist->list[i]));
1208#endif /* NOTDEF */
1209
1210		buf += sprintf(buf, "\n");
1211	}
1212	return obuf;
1213}
1214#endif  /* BCMDBG_PKT */
1215
1216/* iovar table lookup */
1217const bcm_iovar_t*
1218bcm_iovar_lookup(const bcm_iovar_t *table, const char *name)
1219{
1220	const bcm_iovar_t *vi;
1221	const char *lookup_name;
1222
1223	/* skip any ':' delimited option prefixes */
1224	lookup_name = strrchr(name, ':');
1225	if (lookup_name != NULL)
1226		lookup_name++;
1227	else
1228		lookup_name = name;
1229
1230	ASSERT(table);
1231
1232	for (vi = table; vi->name; vi++) {
1233		if (!strcmp(vi->name, lookup_name))
1234			return vi;
1235	}
1236	/* ran to end of table */
1237
1238	return NULL; /* var name not found */
1239}
1240
1241int
1242bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set)
1243{
1244	int bcmerror = 0;
1245
1246	/* length check on io buf */
1247	switch (vi->type) {
1248	case IOVT_BOOL:
1249	case IOVT_INT8:
1250	case IOVT_INT16:
1251	case IOVT_INT32:
1252	case IOVT_UINT8:
1253	case IOVT_UINT16:
1254	case IOVT_UINT32:
1255		/* all integers are int32 sized args at the ioctl interface */
1256		if (len < (int)sizeof(int)) {
1257			bcmerror = BCME_BUFTOOSHORT;
1258		}
1259		break;
1260
1261	case IOVT_BUFFER:
1262		/* buffer must meet minimum length requirement */
1263		if (len < vi->minlen) {
1264			bcmerror = BCME_BUFTOOSHORT;
1265		}
1266		break;
1267
1268	case IOVT_VOID:
1269		if (!set) {
1270			/* Cannot return nil... */
1271			bcmerror = BCME_UNSUPPORTED;
1272		} else if (len) {
1273			/* Set is an action w/o parameters */
1274			bcmerror = BCME_BUFTOOLONG;
1275		}
1276		break;
1277
1278	default:
1279		/* unknown type for length check in iovar info */
1280		ASSERT(0);
1281		bcmerror = BCME_UNSUPPORTED;
1282	}
1283
1284	return bcmerror;
1285}
1286
1287#endif	/* BCMDRIVER */
1288
1289
1290/*******************************************************************************
1291 * crc8
1292 *
1293 * Computes a crc8 over the input data using the polynomial:
1294 *
1295 *       x^8 + x^7 +x^6 + x^4 + x^2 + 1
1296 *
1297 * The caller provides the initial value (either CRC8_INIT_VALUE
1298 * or the previous returned value) to allow for processing of
1299 * discontiguous blocks of data.  When generating the CRC the
1300 * caller is responsible for complementing the final return value
1301 * and inserting it into the byte stream.  When checking, a final
1302 * return value of CRC8_GOOD_VALUE indicates a valid CRC.
1303 *
1304 * Reference: Dallas Semiconductor Application Note 27
1305 *   Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
1306 *     ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
1307 *     ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
1308 *
1309 * ****************************************************************************
1310 */
1311
1312static const uint8 crc8_table[256] = {
1313    0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
1314    0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
1315    0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
1316    0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
1317    0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
1318    0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
1319    0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
1320    0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
1321    0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
1322    0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
1323    0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
1324    0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
1325    0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
1326    0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
1327    0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
1328    0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
1329    0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
1330    0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
1331    0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
1332    0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
1333    0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
1334    0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
1335    0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
1336    0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
1337    0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
1338    0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
1339    0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
1340    0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
1341    0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
1342    0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
1343    0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
1344    0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F
1345};
1346
1347#define CRC_INNER_LOOP(n, c, x) \
1348	(c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff]
1349
1350uint8
1351BCMROMFN(hndcrc8)(
1352	uint8 *pdata,	/* pointer to array of data to process */
1353	uint  nbytes,	/* number of input data bytes to process */
1354	uint8 crc	/* either CRC8_INIT_VALUE or previous return value */
1355)
1356{
1357	/* hard code the crc loop instead of using CRC_INNER_LOOP macro
1358	 * to avoid the undefined and unnecessary (uint8 >> 8) operation.
1359	 */
1360	while (nbytes-- > 0)
1361		crc = crc8_table[(crc ^ *pdata++) & 0xff];
1362
1363	return crc;
1364}
1365
1366/*******************************************************************************
1367 * crc16
1368 *
1369 * Computes a crc16 over the input data using the polynomial:
1370 *
1371 *       x^16 + x^12 +x^5 + 1
1372 *
1373 * The caller provides the initial value (either CRC16_INIT_VALUE
1374 * or the previous returned value) to allow for processing of
1375 * discontiguous blocks of data.  When generating the CRC the
1376 * caller is responsible for complementing the final return value
1377 * and inserting it into the byte stream.  When checking, a final
1378 * return value of CRC16_GOOD_VALUE indicates a valid CRC.
1379 *
1380 * Reference: Dallas Semiconductor Application Note 27
1381 *   Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
1382 *     ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
1383 *     ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
1384 *
1385 * ****************************************************************************
1386 */
1387
1388static const uint16 crc16_table[256] = {
1389    0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
1390    0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
1391    0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
1392    0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
1393    0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
1394    0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
1395    0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
1396    0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
1397    0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
1398    0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
1399    0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
1400    0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
1401    0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
1402    0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
1403    0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
1404    0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
1405    0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
1406    0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
1407    0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
1408    0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
1409    0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
1410    0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
1411    0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
1412    0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
1413    0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
1414    0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
1415    0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
1416    0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
1417    0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
1418    0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
1419    0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
1420    0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
1421};
1422
1423uint16
1424BCMROMFN(hndcrc16)(
1425    uint8 *pdata,  /* pointer to array of data to process */
1426    uint nbytes, /* number of input data bytes to process */
1427    uint16 crc     /* either CRC16_INIT_VALUE or previous return value */
1428)
1429{
1430	while (nbytes-- > 0)
1431		CRC_INNER_LOOP(16, crc, *pdata++);
1432	return crc;
1433}
1434
1435static const uint32 crc32_table[256] = {
1436    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
1437    0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
1438    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
1439    0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
1440    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
1441    0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
1442    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
1443    0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
1444    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
1445    0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
1446    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
1447    0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
1448    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
1449    0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
1450    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
1451    0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
1452    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
1453    0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
1454    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
1455    0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
1456    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
1457    0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
1458    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
1459    0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
1460    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
1461    0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
1462    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
1463    0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
1464    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
1465    0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
1466    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
1467    0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
1468    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
1469    0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
1470    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
1471    0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
1472    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
1473    0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
1474    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
1475    0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
1476    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
1477    0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
1478    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
1479    0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
1480    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
1481    0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
1482    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
1483    0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
1484    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
1485    0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
1486    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
1487    0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
1488    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
1489    0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
1490    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
1491    0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
1492    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
1493    0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
1494    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
1495    0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
1496    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
1497    0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
1498    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
1499    0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
1500};
1501
1502uint32
1503BCMROMFN(hndcrc32)(
1504    uint8 *pdata,  /* pointer to array of data to process */
1505    uint   nbytes, /* number of input data bytes to process */
1506    uint32 crc     /* either CRC32_INIT_VALUE or previous return value */
1507)
1508{
1509	uint8 *pend;
1510#ifdef __mips__
1511	uint8 tmp[4];
1512	ulong *tptr = (ulong *)tmp;
1513
1514	/* in case the beginning of the buffer isn't aligned */
1515	pend = (uint8 *)((uint)(pdata + 3) & 0xfffffffc);
1516	nbytes -= (pend - pdata);
1517	while (pdata < pend)
1518		CRC_INNER_LOOP(32, crc, *pdata++);
1519
1520	/* handle bulk of data as 32-bit words */
1521	pend = pdata + (nbytes & 0xfffffffc);
1522	while (pdata < pend) {
1523		*tptr = *(ulong *)pdata;
1524		pdata += sizeof(ulong *);
1525		CRC_INNER_LOOP(32, crc, tmp[0]);
1526		CRC_INNER_LOOP(32, crc, tmp[1]);
1527		CRC_INNER_LOOP(32, crc, tmp[2]);
1528		CRC_INNER_LOOP(32, crc, tmp[3]);
1529	}
1530
1531	/* 1-3 bytes at end of buffer */
1532	pend = pdata + (nbytes & 0x03);
1533	while (pdata < pend)
1534		CRC_INNER_LOOP(32, crc, *pdata++);
1535#else
1536	pend = pdata + nbytes;
1537	while (pdata < pend)
1538		CRC_INNER_LOOP(32, crc, *pdata++);
1539#endif /* __mips__ */
1540
1541	return crc;
1542}
1543
1544#ifdef notdef
1545#define CLEN 	1499 	/*  CRC Length */
1546#define CBUFSIZ 	(CLEN+4)
1547#define CNBUFS		5 /* # of bufs */
1548
1549void testcrc32(void)
1550{
1551	uint j, k, l;
1552	uint8 *buf;
1553	uint len[CNBUFS];
1554	uint32 crcr;
1555	uint32 crc32tv[CNBUFS] =
1556		{0xd2cb1faa, 0xd385c8fa, 0xf5b4f3f3, 0x55789e20, 0x00343110};
1557
1558	ASSERT((buf = MALLOC(CBUFSIZ*CNBUFS)) != NULL);
1559
1560	/* step through all possible alignments */
1561	for (l = 0; l <= 4; l++) {
1562		for (j = 0; j < CNBUFS; j++) {
1563			len[j] = CLEN;
1564			for (k = 0; k < len[j]; k++)
1565				*(buf + j*CBUFSIZ + (k+l)) = (j+k) & 0xff;
1566		}
1567
1568		for (j = 0; j < CNBUFS; j++) {
1569			crcr = crc32(buf + j*CBUFSIZ + l, len[j], CRC32_INIT_VALUE);
1570			ASSERT(crcr == crc32tv[j]);
1571		}
1572	}
1573
1574	MFREE(buf, CBUFSIZ*CNBUFS);
1575	return;
1576}
1577#endif /* notdef */
1578
1579/*
1580 * Advance from the current 1-byte tag/1-byte length/variable-length value
1581 * triple, to the next, returning a pointer to the next.
1582 * If the current or next TLV is invalid (does not fit in given buffer length),
1583 * NULL is returned.
1584 * *buflen is not modified if the TLV elt parameter is invalid, or is decremented
1585 * by the TLV parameter's length if it is valid.
1586 */
1587bcm_tlv_t *
1588BCMROMFN(bcm_next_tlv)(bcm_tlv_t *elt, int *buflen)
1589{
1590	int len;
1591
1592	/* validate current elt */
1593	if (!bcm_valid_tlv(elt, *buflen))
1594		return NULL;
1595
1596	/* advance to next elt */
1597	len = elt->len;
1598	elt = (bcm_tlv_t*)(elt->data + len);
1599	*buflen -= (2 + len);
1600
1601	/* validate next elt */
1602	if (!bcm_valid_tlv(elt, *buflen))
1603		return NULL;
1604
1605	return elt;
1606}
1607
1608/*
1609 * Traverse a string of 1-byte tag/1-byte length/variable-length value
1610 * triples, returning a pointer to the substring whose first element
1611 * matches tag
1612 */
1613bcm_tlv_t *
1614BCMROMFN(bcm_parse_tlvs)(void *buf, int buflen, uint key)
1615{
1616	bcm_tlv_t *elt;
1617	int totlen;
1618
1619	elt = (bcm_tlv_t*)buf;
1620	totlen = buflen;
1621
1622	/* find tagged parameter */
1623	while (totlen >= 2) {
1624		int len = elt->len;
1625
1626		/* validate remaining totlen */
1627		if ((elt->id == key) && (totlen >= (len + 2)))
1628			return (elt);
1629
1630		elt = (bcm_tlv_t*)((uint8*)elt + (len + 2));
1631		totlen -= (len + 2);
1632	}
1633
1634	return NULL;
1635}
1636
1637/*
1638 * Traverse a string of 1-byte tag/1-byte length/variable-length value
1639 * triples, returning a pointer to the substring whose first element
1640 * matches tag.  Stop parsing when we see an element whose ID is greater
1641 * than the target key.
1642 */
1643bcm_tlv_t *
1644BCMROMFN(bcm_parse_ordered_tlvs)(void *buf, int buflen, uint key)
1645{
1646	bcm_tlv_t *elt;
1647	int totlen;
1648
1649	elt = (bcm_tlv_t*)buf;
1650	totlen = buflen;
1651
1652	/* find tagged parameter */
1653	while (totlen >= 2) {
1654		uint id = elt->id;
1655		int len = elt->len;
1656
1657		/* Punt if we start seeing IDs > than target key */
1658		if (id > key)
1659			return (NULL);
1660
1661		/* validate remaining totlen */
1662		if ((id == key) && (totlen >= (len + 2)))
1663			return (elt);
1664
1665		elt = (bcm_tlv_t*)((uint8*)elt + (len + 2));
1666		totlen -= (len + 2);
1667	}
1668	return NULL;
1669}
1670
1671
1672/* Produce a human-readable string for boardrev */
1673char *
1674bcm_brev_str(uint16 brev, char *buf)
1675{
1676	if (brev < 0x100)
1677		snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);
1678	else
1679		snprintf(buf, 8, "%c%03x", ((brev & 0xf000) == 0x1000) ? 'P' : 'A', brev & 0xfff);
1680
1681	return (buf);
1682}
1683
1684#define BUFSIZE_TODUMP_ATONCE 512 /* Buffer size */
1685
1686/* dump large strings to console */
1687void
1688printfbig(char *buf)
1689{
1690	uint len, max_len;
1691	char c;
1692
1693	len = strlen(buf);
1694
1695	max_len = BUFSIZE_TODUMP_ATONCE;
1696
1697	while (len > max_len) {
1698		c = buf[max_len];
1699		buf[max_len] = '\0';
1700		printf("%s", buf);
1701		buf[max_len] = c;
1702
1703		buf += max_len;
1704		len -= max_len;
1705	}
1706	/* print the remaining string */
1707	printf("%s\n", buf);
1708	return;
1709}
1710
1711/* routine to dump fields in a fileddesc structure */
1712uint
1713bcmdumpfields(readreg_rtn read_rtn, void *arg0, void *arg1, struct fielddesc *fielddesc_array,
1714	char *buf, uint32 bufsize)
1715{
1716	uint  filled_len;
1717	int len;
1718	struct fielddesc *cur_ptr;
1719
1720	filled_len = 0;
1721	cur_ptr = fielddesc_array;
1722
1723	while (bufsize > 1) {
1724		if (cur_ptr->nameandfmt == NULL)
1725			break;
1726		len = snprintf(buf, bufsize, cur_ptr->nameandfmt,
1727		               read_rtn(arg0, arg1, cur_ptr->offset));
1728		/* check for snprintf overflow or error */
1729		if (len < 0 || (uint32)len >= bufsize)
1730			len = bufsize - 1;
1731		buf += len;
1732		bufsize -= len;
1733		filled_len += len;
1734		cur_ptr++;
1735	}
1736	return filled_len;
1737}
1738
1739uint
1740bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)
1741{
1742	uint len;
1743
1744	len = strlen(name) + 1;
1745
1746	if ((len + datalen) > buflen)
1747		return 0;
1748
1749	strncpy(buf, name, buflen);
1750
1751	/* append data onto the end of the name string */
1752	memcpy(&buf[len], data, datalen);
1753	len += datalen;
1754
1755	return len;
1756}
1757
1758
1759/* Quarter dBm units to mW
1760 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
1761 * Table is offset so the last entry is largest mW value that fits in
1762 * a uint16.
1763 */
1764
1765#define QDBM_OFFSET 153		/* Offset for first entry */
1766#define QDBM_TABLE_LEN 40	/* Table size */
1767
1768/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
1769 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
1770 */
1771#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
1772
1773/* Largest mW value that will round down to the last table entry,
1774 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
1775 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
1776 */
1777#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
1778
1779static const uint16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
1780/* qdBm: 	+0 	+1 	+2 	+3 	+4 	+5 	+6 	+7 */
1781/* 153: */      6683,	7079,	7499,	7943,	8414,	8913,	9441,	10000,
1782/* 161: */      10593,	11220,	11885,	12589,	13335,	14125,	14962,	15849,
1783/* 169: */      16788,	17783,	18836,	19953,	21135,	22387,	23714,	25119,
1784/* 177: */      26607,	28184,	29854,	31623,	33497,	35481,	37584,	39811,
1785/* 185: */      42170,	44668,	47315,	50119,	53088,	56234,	59566,	63096
1786};
1787
1788uint16
1789BCMROMFN(bcm_qdbm_to_mw)(uint8 qdbm)
1790{
1791	uint factor = 1;
1792	int idx = qdbm - QDBM_OFFSET;
1793
1794	if (idx > QDBM_TABLE_LEN) {
1795		/* clamp to max uint16 mW value */
1796		return 0xFFFF;
1797	}
1798
1799	/* scale the qdBm index up to the range of the table 0-40
1800	 * where an offset of 40 qdBm equals a factor of 10 mW.
1801	 */
1802	while (idx < 0) {
1803		idx += 40;
1804		factor *= 10;
1805	}
1806
1807	/* return the mW value scaled down to the correct factor of 10,
1808	 * adding in factor/2 to get proper rounding.
1809	 */
1810	return ((nqdBm_to_mW_map[idx] + factor/2) / factor);
1811}
1812
1813uint8
1814BCMROMFN(bcm_mw_to_qdbm)(uint16 mw)
1815{
1816	uint8 qdbm;
1817	int offset;
1818	uint mw_uint = mw;
1819	uint boundary;
1820
1821	/* handle boundary case */
1822	if (mw_uint <= 1)
1823		return 0;
1824
1825	offset = QDBM_OFFSET;
1826
1827	/* move mw into the range of the table */
1828	while (mw_uint < QDBM_TABLE_LOW_BOUND) {
1829		mw_uint *= 10;
1830		offset -= 40;
1831	}
1832
1833	for (qdbm = 0; qdbm < QDBM_TABLE_LEN-1; qdbm++) {
1834		boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm+1] -
1835		                                    nqdBm_to_mW_map[qdbm])/2;
1836		if (mw_uint < boundary) break;
1837	}
1838
1839	qdbm += (uint8)offset;
1840
1841	return (qdbm);
1842}
1843
1844
1845uint
1846BCMROMFN(bcm_bitcount)(uint8 *bitmap, uint length)
1847{
1848	uint bitcount = 0, i;
1849	uint8 tmp;
1850	for (i = 0; i < length; i++) {
1851		tmp = bitmap[i];
1852		while (tmp) {
1853			bitcount++;
1854			tmp &= (tmp - 1);
1855		}
1856	}
1857	return bitcount;
1858}
1859
1860
1861#ifdef BCMDRIVER
1862
1863/* Initialization of bcmstrbuf structure */
1864void
1865bcm_binit(struct bcmstrbuf *b, char *buf, uint size)
1866{
1867	b->origsize = b->size = size;
1868	b->origbuf = b->buf = buf;
1869}
1870
1871/* Buffer sprintf wrapper to guard against buffer overflow */
1872int
1873bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...)
1874{
1875	va_list ap;
1876	int r;
1877
1878	va_start(ap, fmt);
1879	r = vsnprintf(b->buf, b->size, fmt, ap);
1880
1881	/* Non Ansi C99 compliant returns -1,
1882	 * Ansi compliant return r >= b->size,
1883	 * bcmstdlib returns 0, handle all
1884	 */
1885	if ((r == -1) || (r >= (int)b->size) || (r == 0)) {
1886		b->size = 0;
1887	} else {
1888		b->size -= r;
1889		b->buf += r;
1890	}
1891
1892	va_end(ap);
1893
1894	return r;
1895}
1896#endif /* BCMDRIVER */
1897