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