blob: 64e71b9aa4d5dd6af78c5d1192b0da3001559251 [file] [log] [blame]
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <typedefs.h>
18#include <bcmdefs.h>
19#include <stdarg.h>
20#include <osl.h>
21#include <bcmutils.h>
22#include <siutils.h>
23#include <bcmnvram.h>
24#include <bcmendian.h>
25#include <bcmdevs.h>
26#include <proto/ethernet.h>
27#include <proto/802.1d.h>
28#include <proto/802.11.h>
29
30#ifdef WLC_LOW
31/* nvram vars cache */
32static char *nvram_vars = NULL;
33static int vars_len = -1;
34#endif /* WLC_LOW */
35
36/* copy a pkt buffer chain into a buffer */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040037uint pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf)
Henry Ptasinskia9533e72010-09-08 21:04:42 -070038{
39 uint n, ret = 0;
40
41 if (len < 0)
42 len = 4096; /* "infinite" */
43
44 /* skip 'offset' bytes */
45 for (; p && offset; p = PKTNEXT(p)) {
46 if (offset < (uint) PKTLEN(p))
47 break;
48 offset -= PKTLEN(p);
49 }
50
51 if (!p)
52 return 0;
53
54 /* copy the data */
55 for (; p && len; p = PKTNEXT(p)) {
56 n = MIN((uint) PKTLEN(p) - offset, (uint) len);
57 bcopy(PKTDATA(p) + offset, buf, n);
58 buf += n;
59 len -= n;
60 ret += n;
61 offset = 0;
62 }
63
64 return ret;
65}
66
67/* copy a buffer into a pkt buffer chain */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040068uint pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf)
Henry Ptasinskia9533e72010-09-08 21:04:42 -070069{
70 uint n, ret = 0;
71
72 /* skip 'offset' bytes */
73 for (; p && offset; p = PKTNEXT(p)) {
74 if (offset < (uint) PKTLEN(p))
75 break;
76 offset -= PKTLEN(p);
77 }
78
79 if (!p)
80 return 0;
81
82 /* copy the data */
83 for (; p && len; p = PKTNEXT(p)) {
84 n = MIN((uint) PKTLEN(p) - offset, (uint) len);
85 bcopy(buf, PKTDATA(p) + offset, n);
86 buf += n;
87 len -= n;
88 ret += n;
89 offset = 0;
90 }
91
92 return ret;
93}
94
95/* return total length of buffer chain */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040096uint BCMFASTPATH pkttotlen(osl_t *osh, void *p)
Henry Ptasinskia9533e72010-09-08 21:04:42 -070097{
98 uint total;
99
100 total = 0;
101 for (; p; p = PKTNEXT(p))
102 total += PKTLEN(p);
Jason Cooper90ea2292010-09-14 09:45:32 -0400103 return total;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700104}
105
106/* return the last buffer of chained pkt */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400107void *pktlast(osl_t *osh, void *p)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700108{
109 for (; PKTNEXT(p); p = PKTNEXT(p)) ;
110
Jason Cooper90ea2292010-09-14 09:45:32 -0400111 return p;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700112}
113
114/* count segments of a chained packet */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400115uint BCMFASTPATH pktsegcnt(osl_t *osh, void *p)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700116{
117 uint cnt;
118
119 for (cnt = 0; p; p = PKTNEXT(p))
120 cnt++;
121
122 return cnt;
123}
124
125/*
126 * osl multiple-precedence packet queue
127 * hi_prec is always >= the number of the highest non-empty precedence
128 */
129void *BCMFASTPATH pktq_penq(struct pktq *pq, int prec, void *p)
130{
131 struct pktq_prec *q;
132
133 ASSERT(prec >= 0 && prec < pq->num_prec);
134 ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */
135
136 ASSERT(!pktq_full(pq));
137 ASSERT(!pktq_pfull(pq, prec));
138
139 q = &pq->q[prec];
140
141 if (q->head)
142 PKTSETLINK(q->tail, p);
143 else
144 q->head = p;
145
146 q->tail = p;
147 q->len++;
148
149 pq->len++;
150
151 if (pq->hi_prec < prec)
152 pq->hi_prec = (uint8) prec;
153
154 return p;
155}
156
157void *BCMFASTPATH pktq_penq_head(struct pktq *pq, int prec, void *p)
158{
159 struct pktq_prec *q;
160
161 ASSERT(prec >= 0 && prec < pq->num_prec);
162 ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */
163
164 ASSERT(!pktq_full(pq));
165 ASSERT(!pktq_pfull(pq, prec));
166
167 q = &pq->q[prec];
168
169 if (q->head == NULL)
170 q->tail = p;
171
172 PKTSETLINK(p, q->head);
173 q->head = p;
174 q->len++;
175
176 pq->len++;
177
178 if (pq->hi_prec < prec)
179 pq->hi_prec = (uint8) prec;
180
181 return p;
182}
183
184void *BCMFASTPATH pktq_pdeq(struct pktq *pq, int prec)
185{
186 struct pktq_prec *q;
187 void *p;
188
189 ASSERT(prec >= 0 && prec < pq->num_prec);
190
191 q = &pq->q[prec];
192
Jason Cooperca8c1e52010-09-14 09:45:33 -0400193 p = q->head;
194 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700195 return NULL;
196
Jason Cooperca8c1e52010-09-14 09:45:33 -0400197 q->head = PKTLINK(p);
198 if (q->head == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700199 q->tail = NULL;
200
201 q->len--;
202
203 pq->len--;
204
205 PKTSETLINK(p, NULL);
206
207 return p;
208}
209
210void *BCMFASTPATH pktq_pdeq_tail(struct pktq *pq, int prec)
211{
212 struct pktq_prec *q;
213 void *p, *prev;
214
215 ASSERT(prec >= 0 && prec < pq->num_prec);
216
217 q = &pq->q[prec];
218
Jason Cooperca8c1e52010-09-14 09:45:33 -0400219 p = q->head;
220 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700221 return NULL;
222
223 for (prev = NULL; p != q->tail; p = PKTLINK(p))
224 prev = p;
225
226 if (prev)
227 PKTSETLINK(prev, NULL);
228 else
229 q->head = NULL;
230
231 q->tail = prev;
232 q->len--;
233
234 pq->len--;
235
236 return p;
237}
238
239void
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400240pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, ifpkt_cb_t fn,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700241 int arg)
242{
243 struct pktq_prec *q;
244 void *p, *prev = NULL;
245
246 q = &pq->q[prec];
247 p = q->head;
248 while (p) {
249 if (fn == NULL || (*fn) (p, arg)) {
250 bool head = (p == q->head);
251 if (head)
252 q->head = PKTLINK(p);
253 else
254 PKTSETLINK(prev, PKTLINK(p));
255 PKTSETLINK(p, NULL);
256 PKTFREE(osh, p, dir);
257 q->len--;
258 pq->len--;
259 p = (head ? q->head : PKTLINK(prev));
260 } else {
261 prev = p;
262 p = PKTLINK(p);
263 }
264 }
265
266 if (q->head == NULL) {
267 ASSERT(q->len == 0);
268 q->tail = NULL;
269 }
270}
271
272bool BCMFASTPATH pktq_pdel(struct pktq *pq, void *pktbuf, int prec)
273{
274 struct pktq_prec *q;
275 void *p;
276
277 ASSERT(prec >= 0 && prec < pq->num_prec);
278
279 if (!pktbuf)
280 return FALSE;
281
282 q = &pq->q[prec];
283
284 if (q->head == pktbuf) {
Jason Cooperca8c1e52010-09-14 09:45:33 -0400285 q->head = PKTLINK(pktbuf);
286 if (q->head == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700287 q->tail = NULL;
288 } else {
289 for (p = q->head; p && PKTLINK(p) != pktbuf; p = PKTLINK(p)) ;
290 if (p == NULL)
291 return FALSE;
292
293 PKTSETLINK(p, PKTLINK(pktbuf));
294 if (q->tail == pktbuf)
295 q->tail = p;
296 }
297
298 q->len--;
299 pq->len--;
300 PKTSETLINK(pktbuf, NULL);
301 return TRUE;
302}
303
304void pktq_init(struct pktq *pq, int num_prec, int max_len)
305{
306 int prec;
307
308 ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC);
309
310 /* pq is variable size; only zero out what's requested */
311 bzero(pq,
312 OFFSETOF(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
313
314 pq->num_prec = (uint16) num_prec;
315
316 pq->max = (uint16) max_len;
317
318 for (prec = 0; prec < num_prec; prec++)
319 pq->q[prec].max = pq->max;
320}
321
322void *BCMFASTPATH pktq_deq(struct pktq *pq, int *prec_out)
323{
324 struct pktq_prec *q;
325 void *p;
326 int prec;
327
328 if (pq->len == 0)
329 return NULL;
330
331 while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
332 pq->hi_prec--;
333
334 q = &pq->q[prec];
335
Jason Cooperca8c1e52010-09-14 09:45:33 -0400336 p = q->head;
337 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700338 return NULL;
339
Jason Cooperca8c1e52010-09-14 09:45:33 -0400340 q->head = PKTLINK(p);
341 if (q->head == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700342 q->tail = NULL;
343
344 q->len--;
345
346 pq->len--;
347
348 if (prec_out)
349 *prec_out = prec;
350
351 PKTSETLINK(p, NULL);
352
353 return p;
354}
355
356void *BCMFASTPATH pktq_deq_tail(struct pktq *pq, int *prec_out)
357{
358 struct pktq_prec *q;
359 void *p, *prev;
360 int prec;
361
362 if (pq->len == 0)
363 return NULL;
364
365 for (prec = 0; prec < pq->hi_prec; prec++)
366 if (pq->q[prec].head)
367 break;
368
369 q = &pq->q[prec];
370
Jason Cooperca8c1e52010-09-14 09:45:33 -0400371 p = q->head;
372 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700373 return NULL;
374
375 for (prev = NULL; p != q->tail; p = PKTLINK(p))
376 prev = p;
377
378 if (prev)
379 PKTSETLINK(prev, NULL);
380 else
381 q->head = NULL;
382
383 q->tail = prev;
384 q->len--;
385
386 pq->len--;
387
388 if (prec_out)
389 *prec_out = prec;
390
391 PKTSETLINK(p, NULL);
392
393 return p;
394}
395
396void *pktq_peek(struct pktq *pq, int *prec_out)
397{
398 int prec;
399
400 if (pq->len == 0)
401 return NULL;
402
403 while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
404 pq->hi_prec--;
405
406 if (prec_out)
407 *prec_out = prec;
408
Jason Cooper90ea2292010-09-14 09:45:32 -0400409 return pq->q[prec].head;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700410}
411
412void *pktq_peek_tail(struct pktq *pq, int *prec_out)
413{
414 int prec;
415
416 if (pq->len == 0)
417 return NULL;
418
419 for (prec = 0; prec < pq->hi_prec; prec++)
420 if (pq->q[prec].head)
421 break;
422
423 if (prec_out)
424 *prec_out = prec;
425
Jason Cooper90ea2292010-09-14 09:45:32 -0400426 return pq->q[prec].tail;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700427}
428
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400429void pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700430{
431 int prec;
432 for (prec = 0; prec < pq->num_prec; prec++)
433 pktq_pflush(osh, pq, prec, dir, fn, arg);
434 if (fn == NULL)
435 ASSERT(pq->len == 0);
436}
437
438/* Return sum of lengths of a specific set of precedences */
439int pktq_mlen(struct pktq *pq, uint prec_bmp)
440{
441 int prec, len;
442
443 len = 0;
444
445 for (prec = 0; prec <= pq->hi_prec; prec++)
446 if (prec_bmp & (1 << prec))
447 len += pq->q[prec].len;
448
449 return len;
450}
451
452/* Priority dequeue from a specific set of precedences */
453void *BCMFASTPATH pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out)
454{
455 struct pktq_prec *q;
456 void *p;
457 int prec;
458
459 if (pq->len == 0)
460 return NULL;
461
462 while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
463 pq->hi_prec--;
464
465 while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)
466 if (prec-- == 0)
467 return NULL;
468
469 q = &pq->q[prec];
470
Jason Cooperca8c1e52010-09-14 09:45:33 -0400471 p = q->head;
472 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700473 return NULL;
474
Jason Cooperca8c1e52010-09-14 09:45:33 -0400475 q->head = PKTLINK(p);
476 if (q->head == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700477 q->tail = NULL;
478
479 q->len--;
480
481 if (prec_out)
482 *prec_out = prec;
483
484 pq->len--;
485
486 PKTSETLINK(p, NULL);
487
488 return p;
489}
490
491const unsigned char bcm_ctype[] = {
492
493 _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, /* 0-7 */
494 _BCM_C, _BCM_C | _BCM_S, _BCM_C | _BCM_S, _BCM_C | _BCM_S,
495 _BCM_C | _BCM_S, _BCM_C | _BCM_S, _BCM_C,
496 _BCM_C, /* 8-15 */
497 _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, /* 16-23 */
498 _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, _BCM_C, /* 24-31 */
499 _BCM_S | _BCM_SP, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 32-39 */
500 _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 40-47 */
501 _BCM_D, _BCM_D, _BCM_D, _BCM_D, _BCM_D, _BCM_D, _BCM_D, _BCM_D, /* 48-55 */
502 _BCM_D, _BCM_D, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 56-63 */
503 _BCM_P, _BCM_U | _BCM_X, _BCM_U | _BCM_X, _BCM_U | _BCM_X,
504 _BCM_U | _BCM_X, _BCM_U | _BCM_X,
505 _BCM_U | _BCM_X, _BCM_U, /* 64-71 */
506 _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, /* 72-79 */
507 _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, /* 80-87 */
508 _BCM_U, _BCM_U, _BCM_U, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 88-95 */
509 _BCM_P, _BCM_L | _BCM_X, _BCM_L | _BCM_X, _BCM_L | _BCM_X,
510 _BCM_L | _BCM_X, _BCM_L | _BCM_X,
511 _BCM_L | _BCM_X, _BCM_L, /* 96-103 */
512 _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, /* 104-111 */
513 _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, /* 112-119 */
514 _BCM_L, _BCM_L, _BCM_L, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_C, /* 120-127 */
515 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128-143 */
516 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144-159 */
517 _BCM_S | _BCM_SP, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,
518 _BCM_P, _BCM_P, _BCM_P,
519 _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 160-175 */
520 _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,
521 _BCM_P, _BCM_P,
522 _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 176-191 */
523 _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U,
524 _BCM_U, _BCM_U,
525 _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, /* 192-207 */
526 _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_P, _BCM_U,
527 _BCM_U, _BCM_U,
528 _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_L, /* 208-223 */
529 _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L,
530 _BCM_L, _BCM_L,
531 _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, /* 224-239 */
532 _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_P, _BCM_L,
533 _BCM_L, _BCM_L,
534 _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L /* 240-255 */
535};
536
Jason Coopera2627bc2010-09-14 09:45:31 -0400537ulong BCMROMFN(bcm_strtoul) (char *cp, char **endp, uint base)
538{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700539 ulong result, last_result = 0, value;
540 bool minus;
541
542 minus = FALSE;
543
544 while (bcm_isspace(*cp))
545 cp++;
546
547 if (cp[0] == '+')
548 cp++;
549 else if (cp[0] == '-') {
550 minus = TRUE;
551 cp++;
552 }
553
554 if (base == 0) {
555 if (cp[0] == '0') {
556 if ((cp[1] == 'x') || (cp[1] == 'X')) {
557 base = 16;
558 cp = &cp[2];
559 } else {
560 base = 8;
561 cp = &cp[1];
562 }
563 } else
564 base = 10;
565 } else if (base == 16 && (cp[0] == '0')
566 && ((cp[1] == 'x') || (cp[1] == 'X'))) {
567 cp = &cp[2];
568 }
569
570 result = 0;
571
572 while (bcm_isxdigit(*cp) &&
573 (value =
574 bcm_isdigit(*cp) ? *cp - '0' : bcm_toupper(*cp) - 'A' + 10) <
575 base) {
576 result = result * base + value;
577 /* Detected overflow */
578 if (result < last_result && !minus)
579 return (ulong) - 1;
580 last_result = result;
581 cp++;
582 }
583
584 if (minus)
585 result = (ulong) (-(long)result);
586
587 if (endp)
588 *endp = (char *)cp;
589
Jason Cooper90ea2292010-09-14 09:45:32 -0400590 return result;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700591}
592
Jason Coopera2627bc2010-09-14 09:45:31 -0400593int BCMROMFN(bcm_atoi) (char *s)
594{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700595 return (int)bcm_strtoul(s, NULL, 10);
596}
597
598/* return pointer to location of substring 'needle' in 'haystack' */
Jason Coopera2627bc2010-09-14 09:45:31 -0400599char *BCMROMFN(bcmstrstr) (char *haystack, char *needle)
600{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700601 int len, nlen;
602 int i;
603
604 if ((haystack == NULL) || (needle == NULL))
Jason Cooper90ea2292010-09-14 09:45:32 -0400605 return haystack;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700606
607 nlen = strlen(needle);
608 len = strlen(haystack) - nlen + 1;
609
610 for (i = 0; i < len; i++)
611 if (memcmp(needle, &haystack[i], nlen) == 0)
Jason Cooper90ea2292010-09-14 09:45:32 -0400612 return &haystack[i];
613 return NULL;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700614}
615
Jason Coopera2627bc2010-09-14 09:45:31 -0400616char *BCMROMFN(bcmstrcat) (char *dest, const char *src)
617{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700618 char *p;
619
620 p = dest + strlen(dest);
621
622 while ((*p++ = *src++) != '\0') ;
623
Jason Cooper90ea2292010-09-14 09:45:32 -0400624 return dest;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700625}
626
Jason Coopera2627bc2010-09-14 09:45:31 -0400627char *BCMROMFN(bcmstrncat) (char *dest, const char *src, uint size)
628{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700629 char *endp;
630 char *p;
631
632 p = dest + strlen(dest);
633 endp = p + size;
634
635 while (p != endp && (*p++ = *src++) != '\0') ;
636
Jason Cooper90ea2292010-09-14 09:45:32 -0400637 return dest;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700638}
639
640/****************************************************************************
641* Function: bcmstrtok
642*
643* Purpose:
644* Tokenizes a string. This function is conceptually similiar to ANSI C strtok(),
645* but allows strToken() to be used by different strings or callers at the same
646* time. Each call modifies '*string' by substituting a NULL character for the
647* first delimiter that is encountered, and updates 'string' to point to the char
648* after the delimiter. Leading delimiters are skipped.
649*
650* Parameters:
651* string (mod) Ptr to string ptr, updated by token.
652* delimiters (in) Set of delimiter characters.
653* tokdelim (out) Character that delimits the returned token. (May
654* be set to NULL if token delimiter is not required).
655*
656* Returns: Pointer to the next token found. NULL when no more tokens are found.
657*****************************************************************************
658*/
659char *bcmstrtok(char **string, const char *delimiters, char *tokdelim)
660{
661 unsigned char *str;
662 unsigned long map[8];
663 int count;
664 char *nextoken;
665
666 if (tokdelim != NULL) {
667 /* Prime the token delimiter */
668 *tokdelim = '\0';
669 }
670
671 /* Clear control map */
672 for (count = 0; count < 8; count++) {
673 map[count] = 0;
674 }
675
676 /* Set bits in delimiter table */
677 do {
678 map[*delimiters >> 5] |= (1 << (*delimiters & 31));
679 }
680 while (*delimiters++);
681
682 str = (unsigned char *)*string;
683
684 /* Find beginning of token (skip over leading delimiters). Note that
685 * there is no token iff this loop sets str to point to the terminal
686 * null (*str == '\0')
687 */
688 while (((map[*str >> 5] & (1 << (*str & 31))) && *str) || (*str == ' ')) {
689 str++;
690 }
691
692 nextoken = (char *)str;
693
694 /* Find the end of the token. If it is not the end of the string,
695 * put a null there.
696 */
697 for (; *str; str++) {
698 if (map[*str >> 5] & (1 << (*str & 31))) {
699 if (tokdelim != NULL) {
700 *tokdelim = *str;
701 }
702
703 *str++ = '\0';
704 break;
705 }
706 }
707
708 *string = (char *)str;
709
710 /* Determine if a token has been found. */
711 if (nextoken == (char *)str) {
712 return NULL;
713 } else {
714 return nextoken;
715 }
716}
717
718#define xToLower(C) \
719 ((C >= 'A' && C <= 'Z') ? (char)((int)C - (int)'A' + (int)'a') : C)
720
721/****************************************************************************
722* Function: bcmstricmp
723*
724* Purpose: Compare to strings case insensitively.
725*
726* Parameters: s1 (in) First string to compare.
727* s2 (in) Second string to compare.
728*
729* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if
730* t1 > t2, when ignoring case sensitivity.
731*****************************************************************************
732*/
733int bcmstricmp(const char *s1, const char *s2)
734{
735 char dc, sc;
736
737 while (*s2 && *s1) {
738 dc = xToLower(*s1);
739 sc = xToLower(*s2);
740 if (dc < sc)
741 return -1;
742 if (dc > sc)
743 return 1;
744 s1++;
745 s2++;
746 }
747
748 if (*s1 && !*s2)
749 return 1;
750 if (!*s1 && *s2)
751 return -1;
752 return 0;
753}
754
755/****************************************************************************
756* Function: bcmstrnicmp
757*
758* Purpose: Compare to strings case insensitively, upto a max of 'cnt'
759* characters.
760*
761* Parameters: s1 (in) First string to compare.
762* s2 (in) Second string to compare.
763* cnt (in) Max characters to compare.
764*
765* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if
766* t1 > t2, when ignoring case sensitivity.
767*****************************************************************************
768*/
769int bcmstrnicmp(const char *s1, const char *s2, int cnt)
770{
771 char dc, sc;
772
773 while (*s2 && *s1 && cnt) {
774 dc = xToLower(*s1);
775 sc = xToLower(*s2);
776 if (dc < sc)
777 return -1;
778 if (dc > sc)
779 return 1;
780 s1++;
781 s2++;
782 cnt--;
783 }
784
785 if (!cnt)
786 return 0;
787 if (*s1 && !*s2)
788 return 1;
789 if (!*s1 && *s2)
790 return -1;
791 return 0;
792}
793
794/* parse a xx:xx:xx:xx:xx:xx format ethernet address */
Jason Coopera2627bc2010-09-14 09:45:31 -0400795int BCMROMFN(bcm_ether_atoe) (char *p, struct ether_addr *ea)
796{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700797 int i = 0;
798
799 for (;;) {
800 ea->octet[i++] = (char)bcm_strtoul(p, &p, 16);
801 if (!*p++ || i == 6)
802 break;
803 }
804
Jason Cooper90ea2292010-09-14 09:45:32 -0400805 return i == 6;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700806}
807
808char *bcm_ether_ntoa(const struct ether_addr *ea, char *buf)
809{
Andy Shevchenko8dab24c2010-09-13 11:40:27 +0300810 snprintf(buf, 18, "%pM", ea->octet);
Jason Cooper90ea2292010-09-14 09:45:32 -0400811 return buf;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700812}
813
814void bcm_mdelay(uint ms)
815{
816 uint i;
817
818 for (i = 0; i < ms; i++) {
819 OSL_DELAY(1000);
820 }
821}
822
823/*
824 * Search the name=value vars for a specific one and return its value.
825 * Returns NULL if not found.
826 */
827char *getvar(char *vars, const char *name)
828{
829 char *s;
830 int len;
831
832 if (!name)
833 return NULL;
834
835 len = strlen(name);
836 if (len == 0)
837 return NULL;
838
839 /* first look in vars[] */
840 for (s = vars; s && *s;) {
841 if ((bcmp(s, name, len) == 0) && (s[len] == '='))
Jason Cooper90ea2292010-09-14 09:45:32 -0400842 return &s[len + 1];
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700843
844 while (*s++) ;
845 }
846
847 /* then query nvram */
Jason Cooper90ea2292010-09-14 09:45:32 -0400848 return nvram_get(name);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700849}
850
851/*
852 * Search the vars for a specific one and return its value as
853 * an integer. Returns 0 if not found.
854 */
855int getintvar(char *vars, const char *name)
856{
857 char *val;
858
Jason Cooperca8c1e52010-09-14 09:45:33 -0400859 val = getvar(vars, name);
860 if (val == NULL)
Jason Cooper90ea2292010-09-14 09:45:32 -0400861 return 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700862
Jason Cooper90ea2292010-09-14 09:45:32 -0400863 return bcm_strtoul(val, NULL, 0);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700864}
865
866int getintvararray(char *vars, const char *name, uint8 index)
867{
868 char *buf, *endp;
869 int i = 0;
870 int val = 0;
871
Jason Cooperca8c1e52010-09-14 09:45:33 -0400872 buf = getvar(vars, name);
873 if (buf == NULL) {
Jason Cooper90ea2292010-09-14 09:45:32 -0400874 return 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700875 }
876
877 /* table values are always separated by "," or " " */
878 while (*buf != '\0') {
879 val = bcm_strtoul(buf, &endp, 0);
880 if (i == index) {
881 return val;
882 }
883 buf = endp;
884 /* delimiter is ',' */
885 if (*buf == ',')
886 buf++;
887 i++;
888 }
889 return 0;
890}
891
892/* Search for token in comma separated token-string */
893static int findmatch(char *string, char *name)
894{
895 uint len;
896 char *c;
897
898 len = strlen(name);
899 while ((c = strchr(string, ',')) != NULL) {
900 if (len == (uint) (c - string) && !strncmp(string, name, len))
901 return 1;
902 string = c + 1;
903 }
904
Jason Cooper90ea2292010-09-14 09:45:32 -0400905 return !strcmp(string, name);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700906}
907
908/* Return gpio pin number assigned to the named pin
909 *
910 * Variable should be in format:
911 *
912 * gpio<N>=pin_name,pin_name
913 *
914 * This format allows multiple features to share the gpio with mutual
915 * understanding.
916 *
917 * 'def_pin' is returned if a specific gpio is not defined for the requested functionality
918 * and if def_pin is not used by others.
919 */
920uint getgpiopin(char *vars, char *pin_name, uint def_pin)
921{
922 char name[] = "gpioXXXX";
923 char *val;
924 uint pin;
925
926 /* Go thru all possibilities till a match in pin name */
927 for (pin = 0; pin < GPIO_NUMPINS; pin++) {
928 snprintf(name, sizeof(name), "gpio%d", pin);
929 val = getvar(vars, name);
930 if (val && findmatch(val, pin_name))
931 return pin;
932 }
933
934 if (def_pin != GPIO_PIN_NOTDEFINED) {
935 /* make sure the default pin is not used by someone else */
936 snprintf(name, sizeof(name), "gpio%d", def_pin);
937 if (getvar(vars, name)) {
938 def_pin = GPIO_PIN_NOTDEFINED;
939 }
940 }
941 return def_pin;
942}
943
944#if defined(BCMDBG)
945/* pretty hex print a pkt buffer chain */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400946void prpkt(const char *msg, osl_t *osh, void *p0)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700947{
948 void *p;
949
950 if (msg && (msg[0] != '\0'))
951 printf("%s:\n", msg);
952
953 for (p = p0; p; p = PKTNEXT(p))
954 prhex(NULL, PKTDATA(p), PKTLEN(p));
955}
956#endif /* defined(BCMDBG) */
957
958static char bcm_undeferrstr[32];
959static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE;
960
961/* Convert the error codes into related error strings */
962const char *bcmerrorstr(int bcmerror)
963{
964 /* check if someone added a bcmerror code but forgot to add errorstring */
965 ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(bcmerrorstrtable) - 1));
966
967 if (bcmerror > 0 || bcmerror < BCME_LAST) {
968 snprintf(bcm_undeferrstr, sizeof(bcm_undeferrstr),
969 "Undefined error %d", bcmerror);
970 return bcm_undeferrstr;
971 }
972
973 ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN);
974
975 return bcmerrorstrtable[-bcmerror];
976}
977
978#ifdef WLC_LOW
Jason Coopera2627bc2010-09-14 09:45:31 -0400979static void BCMINITFN(bcm_nvram_refresh) (char *flash)
980{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700981 int i;
982 int ret = 0;
983
984 ASSERT(flash != NULL);
985
986 /* default "empty" vars cache */
987 bzero(flash, 2);
988
Jason Cooperca8c1e52010-09-14 09:45:33 -0400989 ret = nvram_getall(flash, NVRAM_SPACE);
990 if (ret)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700991 return;
992
993 /* determine nvram length */
994 for (i = 0; i < NVRAM_SPACE; i++) {
995 if (flash[i] == '\0' && flash[i + 1] == '\0')
996 break;
997 }
998
999 if (i > 1)
1000 vars_len = i + 2;
1001 else
1002 vars_len = 0;
1003}
1004
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001005char *bcm_nvram_vars(uint *length)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001006{
1007#ifndef BCMNVRAMR
1008 /* cache may be stale if nvram is read/write */
1009 if (nvram_vars) {
1010 ASSERT(!bcmreclaimed);
1011 bcm_nvram_refresh(nvram_vars);
1012 }
1013#endif
1014 if (length)
1015 *length = vars_len;
1016 return nvram_vars;
1017}
1018
1019/* copy nvram vars into locally-allocated multi-string array */
Jason Coopera2627bc2010-09-14 09:45:31 -04001020int BCMINITFN(bcm_nvram_cache) (void *sih)
1021{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001022 int ret = 0;
1023 void *osh;
1024 char *flash = NULL;
1025
1026 if (vars_len >= 0) {
1027#ifndef BCMNVRAMR
1028 bcm_nvram_refresh(nvram_vars);
1029#endif
1030 return 0;
1031 }
1032
1033 osh = si_osh((si_t *) sih);
1034
1035 /* allocate memory and read in flash */
Jason Cooperca8c1e52010-09-14 09:45:33 -04001036 flash = MALLOC(osh, NVRAM_SPACE);
1037 if (!flash) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001038 ret = BCME_NOMEM;
1039 goto exit;
1040 }
1041
1042 bcm_nvram_refresh(flash);
1043#ifdef BCMNVRAMR
1044 if (vars_len > 3) {
1045 /* copy into a properly-sized buffer */
Jason Cooperca8c1e52010-09-14 09:45:33 -04001046 nvram_vars = MALLOC(osh, vars_len);
1047 if (!nvram_vars) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001048 ret = BCME_NOMEM;
1049 } else
1050 bcopy(flash, nvram_vars, vars_len);
1051 }
1052 MFREE(osh, flash, NVRAM_SPACE);
1053#else
1054 /* cache must be full size of nvram if read/write */
1055 nvram_vars = flash;
1056#endif /* BCMNVRAMR */
1057
1058 exit:
1059 return ret;
1060}
1061#endif /* WLC_LOW */
1062
1063/* iovar table lookup */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001064const bcm_iovar_t *bcm_iovar_lookup(const bcm_iovar_t *table, const char *name)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001065{
1066 const bcm_iovar_t *vi;
1067 const char *lookup_name;
1068
1069 /* skip any ':' delimited option prefixes */
1070 lookup_name = strrchr(name, ':');
1071 if (lookup_name != NULL)
1072 lookup_name++;
1073 else
1074 lookup_name = name;
1075
1076 ASSERT(table != NULL);
1077
1078 for (vi = table; vi->name; vi++) {
1079 if (!strcmp(vi->name, lookup_name))
1080 return vi;
1081 }
1082 /* ran to end of table */
1083
1084 return NULL; /* var name not found */
1085}
1086
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001087int bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001088{
1089 int bcmerror = 0;
1090
1091 /* length check on io buf */
1092 switch (vi->type) {
1093 case IOVT_BOOL:
1094 case IOVT_INT8:
1095 case IOVT_INT16:
1096 case IOVT_INT32:
1097 case IOVT_UINT8:
1098 case IOVT_UINT16:
1099 case IOVT_UINT32:
1100 /* all integers are int32 sized args at the ioctl interface */
1101 if (len < (int)sizeof(int)) {
1102 bcmerror = BCME_BUFTOOSHORT;
1103 }
1104 break;
1105
1106 case IOVT_BUFFER:
1107 /* buffer must meet minimum length requirement */
1108 if (len < vi->minlen) {
1109 bcmerror = BCME_BUFTOOSHORT;
1110 }
1111 break;
1112
1113 case IOVT_VOID:
1114 if (!set) {
1115 /* Cannot return nil... */
1116 bcmerror = BCME_UNSUPPORTED;
1117 } else if (len) {
1118 /* Set is an action w/o parameters */
1119 bcmerror = BCME_BUFTOOLONG;
1120 }
1121 break;
1122
1123 default:
1124 /* unknown type for length check in iovar info */
1125 ASSERT(0);
1126 bcmerror = BCME_UNSUPPORTED;
1127 }
1128
1129 return bcmerror;
1130}
1131
1132/*******************************************************************************
1133 * crc8
1134 *
1135 * Computes a crc8 over the input data using the polynomial:
1136 *
1137 * x^8 + x^7 +x^6 + x^4 + x^2 + 1
1138 *
1139 * The caller provides the initial value (either CRC8_INIT_VALUE
1140 * or the previous returned value) to allow for processing of
1141 * discontiguous blocks of data. When generating the CRC the
1142 * caller is responsible for complementing the final return value
1143 * and inserting it into the byte stream. When checking, a final
1144 * return value of CRC8_GOOD_VALUE indicates a valid CRC.
1145 *
1146 * Reference: Dallas Semiconductor Application Note 27
1147 * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
1148 * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
1149 * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
1150 *
1151 * ****************************************************************************
1152 */
1153
1154static const uint8 crc8_table[256] = {
1155 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
1156 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
1157 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
1158 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
1159 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
1160 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
1161 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
1162 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
1163 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
1164 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
1165 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
1166 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
1167 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
1168 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
1169 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
1170 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
1171 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
1172 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
1173 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
1174 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
1175 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
1176 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
1177 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
1178 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
1179 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
1180 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
1181 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
1182 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
1183 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
1184 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
1185 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
1186 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F
1187};
1188
1189#define CRC_INNER_LOOP(n, c, x) \
1190 (c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff]
1191
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001192uint8 BCMROMFN(hndcrc8) (uint8 *pdata, /* pointer to array of data to process */
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001193 uint nbytes, /* number of input data bytes to process */
1194 uint8 crc /* either CRC8_INIT_VALUE or previous return value */
1195 ) {
1196 /* hard code the crc loop instead of using CRC_INNER_LOOP macro
1197 * to avoid the undefined and unnecessary (uint8 >> 8) operation.
1198 */
1199 while (nbytes-- > 0)
1200 crc = crc8_table[(crc ^ *pdata++) & 0xff];
1201
1202 return crc;
1203}
1204
1205/*******************************************************************************
1206 * crc16
1207 *
1208 * Computes a crc16 over the input data using the polynomial:
1209 *
1210 * x^16 + x^12 +x^5 + 1
1211 *
1212 * The caller provides the initial value (either CRC16_INIT_VALUE
1213 * or the previous returned value) to allow for processing of
1214 * discontiguous blocks of data. When generating the CRC the
1215 * caller is responsible for complementing the final return value
1216 * and inserting it into the byte stream. When checking, a final
1217 * return value of CRC16_GOOD_VALUE indicates a valid CRC.
1218 *
1219 * Reference: Dallas Semiconductor Application Note 27
1220 * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
1221 * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
1222 * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
1223 *
1224 * ****************************************************************************
1225 */
1226
1227static const uint16 crc16_table[256] = {
1228 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
1229 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
1230 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
1231 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
1232 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
1233 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
1234 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
1235 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
1236 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
1237 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
1238 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
1239 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
1240 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
1241 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
1242 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
1243 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
1244 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
1245 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
1246 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
1247 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
1248 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
1249 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
1250 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
1251 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
1252 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
1253 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
1254 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
1255 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
1256 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
1257 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
1258 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
1259 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
1260};
1261
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001262uint16 BCMROMFN(hndcrc16) (uint8 *pdata, /* pointer to array of data to process */
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001263 uint nbytes, /* number of input data bytes to process */
1264 uint16 crc /* either CRC16_INIT_VALUE or previous return value */
1265 ) {
1266 while (nbytes-- > 0)
1267 CRC_INNER_LOOP(16, crc, *pdata++);
1268 return crc;
1269}
1270
1271/*
1272 * Advance from the current 1-byte tag/1-byte length/variable-length value
1273 * triple, to the next, returning a pointer to the next.
1274 * If the current or next TLV is invalid (does not fit in given buffer length),
1275 * NULL is returned.
1276 * *buflen is not modified if the TLV elt parameter is invalid, or is decremented
1277 * by the TLV parameter's length if it is valid.
1278 */
Jason Coopera2627bc2010-09-14 09:45:31 -04001279bcm_tlv_t *BCMROMFN(bcm_next_tlv) (bcm_tlv_t *elt, int *buflen)
1280{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001281 int len;
1282
1283 /* validate current elt */
1284 if (!bcm_valid_tlv(elt, *buflen))
1285 return NULL;
1286
1287 /* advance to next elt */
1288 len = elt->len;
1289 elt = (bcm_tlv_t *) (elt->data + len);
1290 *buflen -= (2 + len);
1291
1292 /* validate next elt */
1293 if (!bcm_valid_tlv(elt, *buflen))
1294 return NULL;
1295
1296 return elt;
1297}
1298
1299/*
1300 * Traverse a string of 1-byte tag/1-byte length/variable-length value
1301 * triples, returning a pointer to the substring whose first element
1302 * matches tag
1303 */
Jason Coopera2627bc2010-09-14 09:45:31 -04001304bcm_tlv_t *BCMROMFN(bcm_parse_tlvs) (void *buf, int buflen, uint key)
1305{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001306 bcm_tlv_t *elt;
1307 int totlen;
1308
1309 elt = (bcm_tlv_t *) buf;
1310 totlen = buflen;
1311
1312 /* find tagged parameter */
1313 while (totlen >= 2) {
1314 int len = elt->len;
1315
1316 /* validate remaining totlen */
1317 if ((elt->id == key) && (totlen >= (len + 2)))
Jason Cooper90ea2292010-09-14 09:45:32 -04001318 return elt;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001319
1320 elt = (bcm_tlv_t *) ((uint8 *) elt + (len + 2));
1321 totlen -= (len + 2);
1322 }
1323
1324 return NULL;
1325}
1326
1327/*
1328 * Traverse a string of 1-byte tag/1-byte length/variable-length value
1329 * triples, returning a pointer to the substring whose first element
1330 * matches tag. Stop parsing when we see an element whose ID is greater
1331 * than the target key.
1332 */
Jason Coopera2627bc2010-09-14 09:45:31 -04001333bcm_tlv_t *BCMROMFN(bcm_parse_ordered_tlvs) (void *buf, int buflen, uint key)
1334{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001335 bcm_tlv_t *elt;
1336 int totlen;
1337
1338 elt = (bcm_tlv_t *) buf;
1339 totlen = buflen;
1340
1341 /* find tagged parameter */
1342 while (totlen >= 2) {
1343 uint id = elt->id;
1344 int len = elt->len;
1345
1346 /* Punt if we start seeing IDs > than target key */
1347 if (id > key)
Jason Cooper90ea2292010-09-14 09:45:32 -04001348 return NULL;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001349
1350 /* validate remaining totlen */
1351 if ((id == key) && (totlen >= (len + 2)))
Jason Cooper90ea2292010-09-14 09:45:32 -04001352 return elt;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001353
1354 elt = (bcm_tlv_t *) ((uint8 *) elt + (len + 2));
1355 totlen -= (len + 2);
1356 }
1357 return NULL;
1358}
1359
1360#if defined(BCMDBG)
1361int
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001362bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char *buf, int len)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001363{
1364 int i;
1365 char *p = buf;
1366 char hexstr[16];
1367 int slen = 0, nlen = 0;
1368 uint32 bit;
1369 const char *name;
1370
1371 if (len < 2 || !buf)
1372 return 0;
1373
1374 buf[0] = '\0';
1375
1376 for (i = 0; flags != 0; i++) {
1377 bit = bd[i].bit;
1378 name = bd[i].name;
1379 if (bit == 0 && flags != 0) {
1380 /* print any unnamed bits */
1381 snprintf(hexstr, 16, "0x%X", flags);
1382 name = hexstr;
1383 flags = 0; /* exit loop */
1384 } else if ((flags & bit) == 0)
1385 continue;
1386 flags &= ~bit;
1387 nlen = strlen(name);
1388 slen += nlen;
1389 /* count btwn flag space */
1390 if (flags != 0)
1391 slen += 1;
1392 /* need NULL char as well */
1393 if (len <= slen)
1394 break;
1395 /* copy NULL char but don't count it */
1396 strncpy(p, name, nlen + 1);
1397 p += nlen;
1398 /* copy btwn flag space and NULL char */
1399 if (flags != 0)
1400 p += snprintf(p, 2, " ");
1401 len -= slen;
1402 }
1403
1404 /* indicate the str was too short */
1405 if (flags != 0) {
1406 if (len < 2)
1407 p -= 2 - len; /* overwrite last char */
1408 p += snprintf(p, 2, ">");
1409 }
1410
1411 return (int)(p - buf);
1412}
1413
1414/* print bytes formatted as hex to a string. return the resulting string length */
1415int bcm_format_hex(char *str, const void *bytes, int len)
1416{
1417 int i;
1418 char *p = str;
1419 const uint8 *src = (const uint8 *)bytes;
1420
1421 for (i = 0; i < len; i++) {
1422 p += snprintf(p, 3, "%02X", *src);
1423 src++;
1424 }
1425 return (int)(p - str);
1426}
1427#endif /* defined(BCMDBG) */
1428
1429/* pretty hex print a contiguous buffer */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001430void prhex(const char *msg, uchar *buf, uint nbytes)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001431{
1432 char line[128], *p;
1433 int len = sizeof(line);
1434 int nchar;
1435 uint i;
1436
1437 if (msg && (msg[0] != '\0'))
1438 printf("%s:\n", msg);
1439
1440 p = line;
1441 for (i = 0; i < nbytes; i++) {
1442 if (i % 16 == 0) {
1443 nchar = snprintf(p, len, " %04d: ", i); /* line prefix */
1444 p += nchar;
1445 len -= nchar;
1446 }
1447 if (len > 0) {
1448 nchar = snprintf(p, len, "%02x ", buf[i]);
1449 p += nchar;
1450 len -= nchar;
1451 }
1452
1453 if (i % 16 == 15) {
1454 printf("%s\n", line); /* flush line */
1455 p = line;
1456 len = sizeof(line);
1457 }
1458 }
1459
1460 /* flush last partial line */
1461 if (p != line)
1462 printf("%s\n", line);
1463}
1464
1465static const char *crypto_algo_names[] = {
1466 "NONE",
1467 "WEP1",
1468 "TKIP",
1469 "WEP128",
1470 "AES_CCM",
1471 "NALG" "UNDEF",
1472 "UNDEF",
1473 "UNDEF",
1474 "UNDEF"
1475};
1476
1477const char *bcm_crypto_algo_name(uint algo)
1478{
1479 return (algo <
1480 ARRAYSIZE(crypto_algo_names)) ? crypto_algo_names[algo] : "ERR";
1481}
1482
1483#ifdef BCMDBG
1484void deadbeef(void *p, uint len)
1485{
1486 static uint8 meat[] = { 0xde, 0xad, 0xbe, 0xef };
1487
1488 while (len-- > 0) {
1489 *(uint8 *) p = meat[((uintptr) p) & 3];
1490 p = (uint8 *) p + 1;
1491 }
1492}
1493#endif /* BCMDBG */
1494
1495char *bcm_chipname(uint chipid, char *buf, uint len)
1496{
1497 const char *fmt;
1498
1499 fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
1500 snprintf(buf, len, fmt, chipid);
1501 return buf;
1502}
1503
1504/* Produce a human-readable string for boardrev */
1505char *bcm_brev_str(uint32 brev, char *buf)
1506{
1507 if (brev < 0x100)
1508 snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);
1509 else
1510 snprintf(buf, 8, "%c%03x",
1511 ((brev & 0xf000) == 0x1000) ? 'P' : 'A', brev & 0xfff);
1512
Jason Cooper90ea2292010-09-14 09:45:32 -04001513 return buf;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001514}
1515
1516#define BUFSIZE_TODUMP_ATONCE 512 /* Buffer size */
1517
1518/* dump large strings to console */
1519void printbig(char *buf)
1520{
1521 uint len, max_len;
1522 char c;
1523
1524 len = strlen(buf);
1525
1526 max_len = BUFSIZE_TODUMP_ATONCE;
1527
1528 while (len > max_len) {
1529 c = buf[max_len];
1530 buf[max_len] = '\0';
1531 printf("%s", buf);
1532 buf[max_len] = c;
1533
1534 buf += max_len;
1535 len -= max_len;
1536 }
1537 /* print the remaining string */
1538 printf("%s\n", buf);
1539 return;
1540}
1541
1542/* routine to dump fields in a fileddesc structure */
1543uint
1544bcmdumpfields(bcmutl_rdreg_rtn read_rtn, void *arg0, uint arg1,
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001545 struct fielddesc *fielddesc_array, char *buf, uint32 bufsize)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001546{
1547 uint filled_len;
1548 int len;
1549 struct fielddesc *cur_ptr;
1550
1551 filled_len = 0;
1552 cur_ptr = fielddesc_array;
1553
1554 while (bufsize > 1) {
1555 if (cur_ptr->nameandfmt == NULL)
1556 break;
1557 len = snprintf(buf, bufsize, cur_ptr->nameandfmt,
1558 read_rtn(arg0, arg1, cur_ptr->offset));
1559 /* check for snprintf overflow or error */
1560 if (len < 0 || (uint32) len >= bufsize)
1561 len = bufsize - 1;
1562 buf += len;
1563 bufsize -= len;
1564 filled_len += len;
1565 cur_ptr++;
1566 }
1567 return filled_len;
1568}
1569
1570uint bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)
1571{
1572 uint len;
1573
1574 len = strlen(name) + 1;
1575
1576 if ((len + datalen) > buflen)
1577 return 0;
1578
1579 strncpy(buf, name, buflen);
1580
1581 /* append data onto the end of the name string */
1582 memcpy(&buf[len], data, datalen);
1583 len += datalen;
1584
1585 return len;
1586}
1587
1588/* Quarter dBm units to mW
1589 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
1590 * Table is offset so the last entry is largest mW value that fits in
1591 * a uint16.
1592 */
1593
1594#define QDBM_OFFSET 153 /* Offset for first entry */
1595#define QDBM_TABLE_LEN 40 /* Table size */
1596
1597/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
1598 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
1599 */
1600#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
1601
1602/* Largest mW value that will round down to the last table entry,
1603 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
1604 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
1605 */
1606#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
1607
1608static const uint16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
1609/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
1610/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
1611/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
1612/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
1613/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
1614/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
1615};
1616
Jason Coopera2627bc2010-09-14 09:45:31 -04001617uint16 BCMROMFN(bcm_qdbm_to_mw) (uint8 qdbm)
1618{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001619 uint factor = 1;
1620 int idx = qdbm - QDBM_OFFSET;
1621
1622 if (idx >= QDBM_TABLE_LEN) {
1623 /* clamp to max uint16 mW value */
1624 return 0xFFFF;
1625 }
1626
1627 /* scale the qdBm index up to the range of the table 0-40
1628 * where an offset of 40 qdBm equals a factor of 10 mW.
1629 */
1630 while (idx < 0) {
1631 idx += 40;
1632 factor *= 10;
1633 }
1634
1635 /* return the mW value scaled down to the correct factor of 10,
1636 * adding in factor/2 to get proper rounding.
1637 */
Jason Cooper90ea2292010-09-14 09:45:32 -04001638 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001639}
1640
Jason Coopera2627bc2010-09-14 09:45:31 -04001641uint8 BCMROMFN(bcm_mw_to_qdbm) (uint16 mw)
1642{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001643 uint8 qdbm;
1644 int offset;
1645 uint mw_uint = mw;
1646 uint boundary;
1647
1648 /* handle boundary case */
1649 if (mw_uint <= 1)
1650 return 0;
1651
1652 offset = QDBM_OFFSET;
1653
1654 /* move mw into the range of the table */
1655 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
1656 mw_uint *= 10;
1657 offset -= 40;
1658 }
1659
1660 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
1661 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
1662 nqdBm_to_mW_map[qdbm]) / 2;
1663 if (mw_uint < boundary)
1664 break;
1665 }
1666
1667 qdbm += (uint8) offset;
1668
Jason Cooper90ea2292010-09-14 09:45:32 -04001669 return qdbm;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001670}
1671
Jason Coopera2627bc2010-09-14 09:45:31 -04001672uint BCMROMFN(bcm_bitcount) (uint8 *bitmap, uint length)
1673{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001674 uint bitcount = 0, i;
1675 uint8 tmp;
1676 for (i = 0; i < length; i++) {
1677 tmp = bitmap[i];
1678 while (tmp) {
1679 bitcount++;
1680 tmp &= (tmp - 1);
1681 }
1682 }
1683 return bitcount;
1684}
1685
1686/* Initialization of bcmstrbuf structure */
1687void bcm_binit(struct bcmstrbuf *b, char *buf, uint size)
1688{
1689 b->origsize = b->size = size;
1690 b->origbuf = b->buf = buf;
1691}
1692
1693/* Buffer sprintf wrapper to guard against buffer overflow */
1694int bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...)
1695{
1696 va_list ap;
1697 int r;
1698
1699 va_start(ap, fmt);
1700 r = vsnprintf(b->buf, b->size, fmt, ap);
1701
1702 /* Non Ansi C99 compliant returns -1,
1703 * Ansi compliant return r >= b->size,
1704 * bcmstdlib returns 0, handle all
1705 */
1706 if ((r == -1) || (r >= (int)b->size) || (r == 0)) {
1707 b->size = 0;
1708 } else {
1709 b->size -= r;
1710 b->buf += r;
1711 }
1712
1713 va_end(ap);
1714
1715 return r;
1716}
1717
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001718void bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001719{
1720 int i;
1721
1722 for (i = 0; i < num_bytes; i++) {
1723 num[i] += amount;
1724 if (num[i] >= amount)
1725 break;
1726 amount = 1;
1727 }
1728}
1729
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001730int bcm_cmp_bytes(uchar *arg1, uchar *arg2, uint8 nbytes)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001731{
1732 int i;
1733
1734 for (i = nbytes - 1; i >= 0; i--) {
1735 if (arg1[i] != arg2[i])
Jason Cooper90ea2292010-09-14 09:45:32 -04001736 return arg1[i] - arg2[i];
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001737 }
1738 return 0;
1739}
1740
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001741void bcm_print_bytes(char *name, const uchar *data, int len)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001742{
1743 int i;
1744 int per_line = 0;
1745
1746 printf("%s: %d\n", name ? name : "", len);
1747 for (i = 0; i < len; i++) {
1748 printf("%02x ", *data++);
1749 per_line++;
1750 if (per_line == 16) {
1751 per_line = 0;
1752 printf("\n");
1753 }
1754 }
1755 printf("\n");
1756}
1757
1758#if defined(BCMDBG)
1759#define SSID_FMT_BUF_LEN ((4 * DOT11_MAX_SSID_LEN) + 1)
1760int bcm_format_ssid(char *buf, const uchar ssid[], uint ssid_len)
1761{
1762 uint i, c;
1763 char *p = buf;
1764 char *endp = buf + SSID_FMT_BUF_LEN;
1765
1766 if (ssid_len > DOT11_MAX_SSID_LEN)
1767 ssid_len = DOT11_MAX_SSID_LEN;
1768
1769 for (i = 0; i < ssid_len; i++) {
1770 c = (uint) ssid[i];
1771 if (c == '\\') {
1772 *p++ = '\\';
1773 *p++ = '\\';
1774 } else if (bcm_isprint((uchar) c)) {
1775 *p++ = (char)c;
1776 } else {
1777 p += snprintf(p, (endp - p), "\\x%02X", c);
1778 }
1779 }
1780 *p = '\0';
1781 ASSERT(p < endp);
1782
1783 return (int)(p - buf);
1784}
1785#endif /* defined(BCMDBG) */