blob: 7ac64b6c00f61e04498dcb02b20af528ab663f62 [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
Andy Shevchenko48c51a82010-09-15 12:47:18 +030017#include <linux/ctype.h>
18#include <linux/kernel.h>
Brett Rudley33279892010-10-01 18:03:27 -070019#include <linux/string.h>
Greg Kroah-Hartmana1c16ed2010-10-21 11:17:44 -070020#include <bcmdefs.h>
21#include <stdarg.h>
22#include <osl.h>
Brett Rudley33279892010-10-01 18:03:27 -070023#include <linuxver.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070024#include <bcmutils.h>
25#include <siutils.h>
26#include <bcmnvram.h>
27#include <bcmendian.h>
28#include <bcmdevs.h>
29#include <proto/ethernet.h>
nohee koea3b8a22010-10-09 10:34:38 -070030#include <proto/vlan.h>
31#include <proto/bcmip.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070032#include <proto/802.1d.h>
33#include <proto/802.11.h>
34
Henry Ptasinskia9533e72010-09-08 21:04:42 -070035
nohee koea3b8a22010-10-09 10:34:38 -070036/* copy a buffer into a pkt buffer chain */
37uint pktfrombuf(osl_t *osh, void *p, uint offset, int len, unsigned char *buf)
38{
39 uint n, ret = 0;
40
41 /* skip 'offset' bytes */
42 for (; p && offset; p = PKTNEXT(p)) {
43 if (offset < (uint) PKTLEN(p))
44 break;
45 offset -= PKTLEN(p);
46 }
47
48 if (!p)
49 return 0;
50
51 /* copy the data */
52 for (; p && len; p = PKTNEXT(p)) {
53 n = min((uint) PKTLEN(p) - offset, (uint) len);
54 bcopy(buf, PKTDATA(p) + offset, n);
55 buf += n;
56 len -= n;
57 ret += n;
58 offset = 0;
59 }
60
61 return ret;
62}
Henry Ptasinskia9533e72010-09-08 21:04:42 -070063/* return total length of buffer chain */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040064uint BCMFASTPATH pkttotlen(osl_t *osh, void *p)
Henry Ptasinskia9533e72010-09-08 21:04:42 -070065{
66 uint total;
67
68 total = 0;
69 for (; p; p = PKTNEXT(p))
70 total += PKTLEN(p);
Jason Cooper90ea2292010-09-14 09:45:32 -040071 return total;
Henry Ptasinskia9533e72010-09-08 21:04:42 -070072}
73
Henry Ptasinskia9533e72010-09-08 21:04:42 -070074/*
75 * osl multiple-precedence packet queue
76 * hi_prec is always >= the number of the highest non-empty precedence
77 */
78void *BCMFASTPATH pktq_penq(struct pktq *pq, int prec, void *p)
79{
80 struct pktq_prec *q;
81
82 ASSERT(prec >= 0 && prec < pq->num_prec);
83 ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */
84
85 ASSERT(!pktq_full(pq));
86 ASSERT(!pktq_pfull(pq, prec));
87
88 q = &pq->q[prec];
89
90 if (q->head)
91 PKTSETLINK(q->tail, p);
92 else
93 q->head = p;
94
95 q->tail = p;
96 q->len++;
97
98 pq->len++;
99
100 if (pq->hi_prec < prec)
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700101 pq->hi_prec = (u8) prec;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700102
103 return p;
104}
105
106void *BCMFASTPATH pktq_penq_head(struct pktq *pq, int prec, void *p)
107{
108 struct pktq_prec *q;
109
110 ASSERT(prec >= 0 && prec < pq->num_prec);
111 ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */
112
113 ASSERT(!pktq_full(pq));
114 ASSERT(!pktq_pfull(pq, prec));
115
116 q = &pq->q[prec];
117
118 if (q->head == NULL)
119 q->tail = p;
120
121 PKTSETLINK(p, q->head);
122 q->head = p;
123 q->len++;
124
125 pq->len++;
126
127 if (pq->hi_prec < prec)
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700128 pq->hi_prec = (u8) prec;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700129
130 return p;
131}
132
133void *BCMFASTPATH pktq_pdeq(struct pktq *pq, int prec)
134{
135 struct pktq_prec *q;
136 void *p;
137
138 ASSERT(prec >= 0 && prec < pq->num_prec);
139
140 q = &pq->q[prec];
141
Jason Cooperca8c1e52010-09-14 09:45:33 -0400142 p = q->head;
143 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700144 return NULL;
145
Jason Cooperca8c1e52010-09-14 09:45:33 -0400146 q->head = PKTLINK(p);
147 if (q->head == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700148 q->tail = NULL;
149
150 q->len--;
151
152 pq->len--;
153
154 PKTSETLINK(p, NULL);
155
156 return p;
157}
158
159void *BCMFASTPATH pktq_pdeq_tail(struct pktq *pq, int prec)
160{
161 struct pktq_prec *q;
162 void *p, *prev;
163
164 ASSERT(prec >= 0 && prec < pq->num_prec);
165
166 q = &pq->q[prec];
167
Jason Cooperca8c1e52010-09-14 09:45:33 -0400168 p = q->head;
169 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700170 return NULL;
171
172 for (prev = NULL; p != q->tail; p = PKTLINK(p))
173 prev = p;
174
175 if (prev)
176 PKTSETLINK(prev, NULL);
177 else
178 q->head = NULL;
179
180 q->tail = prev;
181 q->len--;
182
183 pq->len--;
184
185 return p;
186}
187
nohee koea3b8a22010-10-09 10:34:38 -0700188#ifdef BRCM_FULLMAC
189void pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir)
190{
191 struct pktq_prec *q;
192 void *p;
193
194 q = &pq->q[prec];
195 p = q->head;
196 while (p) {
197 q->head = PKTLINK(p);
198 PKTSETLINK(p, NULL);
199 PKTFREE(osh, p, dir);
200 q->len--;
201 pq->len--;
202 p = q->head;
203 }
204 ASSERT(q->len == 0);
205 q->tail = NULL;
206}
207
208void pktq_flush(osl_t *osh, struct pktq *pq, bool dir)
209{
210 int prec;
211 for (prec = 0; prec < pq->num_prec; prec++)
212 pktq_pflush(osh, pq, prec, dir);
213 ASSERT(pq->len == 0);
214}
215#else /* !BRCM_FULLMAC */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700216void
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400217pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, ifpkt_cb_t fn,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700218 int arg)
219{
220 struct pktq_prec *q;
221 void *p, *prev = NULL;
222
223 q = &pq->q[prec];
224 p = q->head;
225 while (p) {
226 if (fn == NULL || (*fn) (p, arg)) {
227 bool head = (p == q->head);
228 if (head)
229 q->head = PKTLINK(p);
230 else
231 PKTSETLINK(prev, PKTLINK(p));
232 PKTSETLINK(p, NULL);
233 PKTFREE(osh, p, dir);
234 q->len--;
235 pq->len--;
236 p = (head ? q->head : PKTLINK(prev));
237 } else {
238 prev = p;
239 p = PKTLINK(p);
240 }
241 }
242
243 if (q->head == NULL) {
244 ASSERT(q->len == 0);
245 q->tail = NULL;
246 }
247}
248
nohee koea3b8a22010-10-09 10:34:38 -0700249void pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg)
250{
251 int prec;
252 for (prec = 0; prec < pq->num_prec; prec++)
253 pktq_pflush(osh, pq, prec, dir, fn, arg);
254 if (fn == NULL)
255 ASSERT(pq->len == 0);
256}
257#endif /* BRCM_FULLMAC */
258
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700259void pktq_init(struct pktq *pq, int num_prec, int max_len)
260{
261 int prec;
262
263 ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC);
264
265 /* pq is variable size; only zero out what's requested */
266 bzero(pq,
Greg Kroah-Hartmance0f1b82010-10-08 11:44:45 -0700267 offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700268
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700269 pq->num_prec = (u16) num_prec;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700270
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700271 pq->max = (u16) max_len;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700272
273 for (prec = 0; prec < num_prec; prec++)
274 pq->q[prec].max = pq->max;
275}
276
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700277void *pktq_peek_tail(struct pktq *pq, int *prec_out)
278{
279 int prec;
280
281 if (pq->len == 0)
282 return NULL;
283
284 for (prec = 0; prec < pq->hi_prec; prec++)
285 if (pq->q[prec].head)
286 break;
287
288 if (prec_out)
289 *prec_out = prec;
290
Jason Cooper90ea2292010-09-14 09:45:32 -0400291 return pq->q[prec].tail;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700292}
293
nohee koea3b8a22010-10-09 10:34:38 -0700294/* Return sum of lengths of a specific set of precedences */
295int pktq_mlen(struct pktq *pq, uint prec_bmp)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700296{
nohee koea3b8a22010-10-09 10:34:38 -0700297 int prec, len;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700298
nohee koea3b8a22010-10-09 10:34:38 -0700299 len = 0;
300
301 for (prec = 0; prec <= pq->hi_prec; prec++)
302 if (prec_bmp & (1 << prec))
303 len += pq->q[prec].len;
304
305 return len;
306}
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700307/* Priority dequeue from a specific set of precedences */
308void *BCMFASTPATH pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out)
309{
310 struct pktq_prec *q;
311 void *p;
312 int prec;
313
314 if (pq->len == 0)
315 return NULL;
316
317 while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
318 pq->hi_prec--;
319
320 while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)
321 if (prec-- == 0)
322 return NULL;
323
324 q = &pq->q[prec];
325
Jason Cooperca8c1e52010-09-14 09:45:33 -0400326 p = q->head;
327 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700328 return NULL;
329
Jason Cooperca8c1e52010-09-14 09:45:33 -0400330 q->head = PKTLINK(p);
331 if (q->head == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700332 q->tail = NULL;
333
334 q->len--;
335
336 if (prec_out)
337 *prec_out = prec;
338
339 pq->len--;
340
341 PKTSETLINK(p, NULL);
342
343 return p;
344}
345
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700346/* parse a xx:xx:xx:xx:xx:xx format ethernet address */
nohee koea3b8a22010-10-09 10:34:38 -0700347int bcm_ether_atoe(char *p, struct ether_addr *ea)
Jason Coopera2627bc2010-09-14 09:45:31 -0400348{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700349 int i = 0;
350
351 for (;;) {
Andy Shevchenko48c51a82010-09-15 12:47:18 +0300352 ea->octet[i++] = (char)simple_strtoul(p, &p, 16);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700353 if (!*p++ || i == 6)
354 break;
355 }
356
Jason Cooper90ea2292010-09-14 09:45:32 -0400357 return i == 6;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700358}
359
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700360/*
361 * Search the name=value vars for a specific one and return its value.
362 * Returns NULL if not found.
363 */
364char *getvar(char *vars, const char *name)
365{
366 char *s;
367 int len;
368
369 if (!name)
370 return NULL;
371
372 len = strlen(name);
373 if (len == 0)
374 return NULL;
375
376 /* first look in vars[] */
377 for (s = vars; s && *s;) {
378 if ((bcmp(s, name, len) == 0) && (s[len] == '='))
Jason Cooper90ea2292010-09-14 09:45:32 -0400379 return &s[len + 1];
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700380
Jason Cooper62145822010-09-14 09:45:34 -0400381 while (*s++)
382 ;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700383 }
nohee koea3b8a22010-10-09 10:34:38 -0700384#ifdef BRCM_FULLMAC
385 return NULL;
386#else
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700387 /* then query nvram */
Jason Cooper90ea2292010-09-14 09:45:32 -0400388 return nvram_get(name);
nohee koea3b8a22010-10-09 10:34:38 -0700389#endif
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700390}
391
392/*
393 * Search the vars for a specific one and return its value as
394 * an integer. Returns 0 if not found.
395 */
396int getintvar(char *vars, const char *name)
397{
398 char *val;
399
Jason Cooperca8c1e52010-09-14 09:45:33 -0400400 val = getvar(vars, name);
401 if (val == NULL)
Jason Cooper90ea2292010-09-14 09:45:32 -0400402 return 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700403
Andy Shevchenko48c51a82010-09-15 12:47:18 +0300404 return simple_strtoul(val, NULL, 0);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700405}
406
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700407#if defined(BCMDBG)
408/* pretty hex print a pkt buffer chain */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400409void prpkt(const char *msg, osl_t *osh, void *p0)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700410{
411 void *p;
412
413 if (msg && (msg[0] != '\0'))
414 printf("%s:\n", msg);
415
416 for (p = p0; p; p = PKTNEXT(p))
417 prhex(NULL, PKTDATA(p), PKTLEN(p));
418}
419#endif /* defined(BCMDBG) */
420
nohee koea3b8a22010-10-09 10:34:38 -0700421/* Takes an Ethernet frame and sets out-of-bound PKTPRIO.
422 * Also updates the inplace vlan tag if requested.
423 * For debugging, it returns an indication of what it did.
424 */
425uint pktsetprio(void *pkt, bool update_vtag)
426{
427 struct ether_header *eh;
428 struct ethervlan_header *evh;
429 u8 *pktdata;
430 int priority = 0;
431 int rc = 0;
432
433 pktdata = (u8 *) PKTDATA(pkt);
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -0700434 ASSERT(IS_ALIGNED((unsigned long)pktdata, sizeof(u16)));
nohee koea3b8a22010-10-09 10:34:38 -0700435
436 eh = (struct ether_header *)pktdata;
437
438 if (ntoh16(eh->ether_type) == ETHER_TYPE_8021Q) {
439 u16 vlan_tag;
440 int vlan_prio, dscp_prio = 0;
441
442 evh = (struct ethervlan_header *)eh;
443
444 vlan_tag = ntoh16(evh->vlan_tag);
445 vlan_prio = (int)(vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK;
446
447 if (ntoh16(evh->ether_type) == ETHER_TYPE_IP) {
448 u8 *ip_body =
449 pktdata + sizeof(struct ethervlan_header);
450 u8 tos_tc = IP_TOS(ip_body);
451 dscp_prio = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);
452 }
453
454 /* DSCP priority gets precedence over 802.1P (vlan tag) */
455 if (dscp_prio != 0) {
456 priority = dscp_prio;
457 rc |= PKTPRIO_VDSCP;
458 } else {
459 priority = vlan_prio;
460 rc |= PKTPRIO_VLAN;
461 }
462 /*
463 * If the DSCP priority is not the same as the VLAN priority,
464 * then overwrite the priority field in the vlan tag, with the
465 * DSCP priority value. This is required for Linux APs because
466 * the VLAN driver on Linux, overwrites the skb->priority field
467 * with the priority value in the vlan tag
468 */
469 if (update_vtag && (priority != vlan_prio)) {
470 vlan_tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT);
471 vlan_tag |= (u16) priority << VLAN_PRI_SHIFT;
472 evh->vlan_tag = hton16(vlan_tag);
473 rc |= PKTPRIO_UPD;
474 }
475 } else if (ntoh16(eh->ether_type) == ETHER_TYPE_IP) {
476 u8 *ip_body = pktdata + sizeof(struct ether_header);
477 u8 tos_tc = IP_TOS(ip_body);
478 priority = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);
479 rc |= PKTPRIO_DSCP;
480 }
481
482 ASSERT(priority >= 0 && priority <= MAXPRIO);
483 PKTSETPRIO(pkt, priority);
484 return rc | priority;
485}
486
487static char bcm_undeferrstr[BCME_STRLEN];
488
489static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE;
490
491/* Convert the error codes into related error strings */
492const char *bcmerrorstr(int bcmerror)
493{
494 /* check if someone added a bcmerror code but
495 forgot to add errorstring */
496 ASSERT(ABS(BCME_LAST) == (ARRAY_SIZE(bcmerrorstrtable) - 1));
497
498 if (bcmerror > 0 || bcmerror < BCME_LAST) {
499 snprintf(bcm_undeferrstr, BCME_STRLEN, "Undefined error %d",
500 bcmerror);
501 return bcm_undeferrstr;
502 }
503
504 ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN);
505
506 return bcmerrorstrtable[-bcmerror];
507}
508
509/* iovar table lookup */
510const bcm_iovar_t *bcm_iovar_lookup(const bcm_iovar_t *table, const char *name)
511{
512 const bcm_iovar_t *vi;
513 const char *lookup_name;
514
515 /* skip any ':' delimited option prefixes */
516 lookup_name = strrchr(name, ':');
517 if (lookup_name != NULL)
518 lookup_name++;
519 else
520 lookup_name = name;
521
522 ASSERT(table != NULL);
523
524 for (vi = table; vi->name; vi++) {
525 if (!strcmp(vi->name, lookup_name))
526 return vi;
527 }
528 /* ran to end of table */
529
530 return NULL; /* var name not found */
531}
532
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400533int bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700534{
535 int bcmerror = 0;
536
537 /* length check on io buf */
538 switch (vi->type) {
539 case IOVT_BOOL:
540 case IOVT_INT8:
541 case IOVT_INT16:
542 case IOVT_INT32:
543 case IOVT_UINT8:
544 case IOVT_UINT16:
545 case IOVT_UINT32:
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700546 /* all integers are s32 sized args at the ioctl interface */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700547 if (len < (int)sizeof(int)) {
548 bcmerror = BCME_BUFTOOSHORT;
549 }
550 break;
551
552 case IOVT_BUFFER:
553 /* buffer must meet minimum length requirement */
554 if (len < vi->minlen) {
555 bcmerror = BCME_BUFTOOSHORT;
556 }
557 break;
558
559 case IOVT_VOID:
560 if (!set) {
561 /* Cannot return nil... */
562 bcmerror = BCME_UNSUPPORTED;
563 } else if (len) {
564 /* Set is an action w/o parameters */
565 bcmerror = BCME_BUFTOOLONG;
566 }
567 break;
568
569 default:
570 /* unknown type for length check in iovar info */
571 ASSERT(0);
572 bcmerror = BCME_UNSUPPORTED;
573 }
574
575 return bcmerror;
576}
577
578/*******************************************************************************
579 * crc8
580 *
581 * Computes a crc8 over the input data using the polynomial:
582 *
583 * x^8 + x^7 +x^6 + x^4 + x^2 + 1
584 *
585 * The caller provides the initial value (either CRC8_INIT_VALUE
586 * or the previous returned value) to allow for processing of
587 * discontiguous blocks of data. When generating the CRC the
588 * caller is responsible for complementing the final return value
589 * and inserting it into the byte stream. When checking, a final
590 * return value of CRC8_GOOD_VALUE indicates a valid CRC.
591 *
592 * Reference: Dallas Semiconductor Application Note 27
593 * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
594 * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
595 * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
596 *
597 * ****************************************************************************
598 */
599
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700600static const u8 crc8_table[256] = {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700601 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
602 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
603 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
604 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
605 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
606 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
607 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
608 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
609 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
610 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
611 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
612 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
613 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
614 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
615 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
616 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
617 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
618 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
619 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
620 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
621 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
622 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
623 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
624 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
625 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
626 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
627 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
628 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
629 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
630 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
631 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
632 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F
633};
634
635#define CRC_INNER_LOOP(n, c, x) \
Jason Cooper0d706ef2010-09-14 09:45:39 -0400636 ((c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff])
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700637
nohee koea3b8a22010-10-09 10:34:38 -0700638u8 hndcrc8(u8 *pdata, /* pointer to array of data to process */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700639 uint nbytes, /* number of input data bytes to process */
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700640 u8 crc /* either CRC8_INIT_VALUE or previous return value */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700641 ) {
642 /* hard code the crc loop instead of using CRC_INNER_LOOP macro
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700643 * to avoid the undefined and unnecessary (u8 >> 8) operation.
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700644 */
645 while (nbytes-- > 0)
646 crc = crc8_table[(crc ^ *pdata++) & 0xff];
647
648 return crc;
649}
650
651/*******************************************************************************
652 * crc16
653 *
654 * Computes a crc16 over the input data using the polynomial:
655 *
656 * x^16 + x^12 +x^5 + 1
657 *
658 * The caller provides the initial value (either CRC16_INIT_VALUE
659 * or the previous returned value) to allow for processing of
660 * discontiguous blocks of data. When generating the CRC the
661 * caller is responsible for complementing the final return value
662 * and inserting it into the byte stream. When checking, a final
663 * return value of CRC16_GOOD_VALUE indicates a valid CRC.
664 *
665 * Reference: Dallas Semiconductor Application Note 27
666 * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
667 * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
668 * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
669 *
670 * ****************************************************************************
671 */
672
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700673static const u16 crc16_table[256] = {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700674 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
675 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
676 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
677 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
678 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
679 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
680 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
681 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
682 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
683 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
684 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
685 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
686 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
687 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
688 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
689 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
690 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
691 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
692 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
693 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
694 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
695 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
696 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
697 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
698 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
699 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
700 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
701 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
702 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
703 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
704 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
705 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
706};
707
nohee koea3b8a22010-10-09 10:34:38 -0700708u16 hndcrc16(u8 *pdata, /* pointer to array of data to process */
709 uint nbytes, /* number of input data bytes to process */
710 u16 crc /* either CRC16_INIT_VALUE or previous return value */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700711 ) {
712 while (nbytes-- > 0)
713 CRC_INNER_LOOP(16, crc, *pdata++);
714 return crc;
715}
716
nohee koea3b8a22010-10-09 10:34:38 -0700717static const u32 crc32_table[256] = {
718 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
719 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
720 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
721 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
722 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
723 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
724 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
725 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
726 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
727 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
728 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
729 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
730 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
731 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
732 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
733 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
734 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
735 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
736 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
737 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
738 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
739 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
740 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
741 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
742 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
743 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
744 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
745 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
746 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
747 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
748 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
749 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
750 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
751 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
752 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
753 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
754 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
755 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
756 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
757 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
758 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
759 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
760 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
761 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
762 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
763 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
764 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
765 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
766 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
767 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
768 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
769 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
770 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
771 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
772 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
773 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
774 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
775 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
776 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
777 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
778 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
779 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
780 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
781 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
782};
783
784u32 hndcrc32(u8 *pdata, /* pointer to array of data to process */
785 uint nbytes, /* number of input data bytes to process */
786 u32 crc /* either CRC32_INIT_VALUE or previous
787 return value */
788)
789{
790 u8 *pend;
791#ifdef __mips__
792 u8 tmp[4];
793 unsigned long *tptr = (unsigned long *) tmp;
794
795 /* in case the beginning of the buffer isn't aligned */
796 pend = (u8 *) ((uint) (pdata + 3) & 0xfffffffc);
797 nbytes -= (pend - pdata);
798 while (pdata < pend)
799 CRC_INNER_LOOP(32, crc, *pdata++);
800
801 /* handle bulk of data as 32-bit words */
802 pend = pdata + (nbytes & 0xfffffffc);
803 while (pdata < pend) {
804 *tptr = *(unsigned long *) pdata;
805 pdata += sizeof(unsigned long *);
806 CRC_INNER_LOOP(32, crc, tmp[0]);
807 CRC_INNER_LOOP(32, crc, tmp[1]);
808 CRC_INNER_LOOP(32, crc, tmp[2]);
809 CRC_INNER_LOOP(32, crc, tmp[3]);
810 }
811
812 /* 1-3 bytes at end of buffer */
813 pend = pdata + (nbytes & 0x03);
814 while (pdata < pend)
815 CRC_INNER_LOOP(32, crc, *pdata++);
816#else
817 pend = pdata + nbytes;
818 while (pdata < pend)
819 CRC_INNER_LOOP(32, crc, *pdata++);
820#endif /* __mips__ */
821
822 return crc;
823}
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700824/*
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700825 * Traverse a string of 1-byte tag/1-byte length/variable-length value
826 * triples, returning a pointer to the substring whose first element
827 * matches tag
828 */
nohee koea3b8a22010-10-09 10:34:38 -0700829bcm_tlv_t *bcm_parse_tlvs(void *buf, int buflen, uint key)
Jason Coopera2627bc2010-09-14 09:45:31 -0400830{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700831 bcm_tlv_t *elt;
832 int totlen;
833
834 elt = (bcm_tlv_t *) buf;
835 totlen = buflen;
836
837 /* find tagged parameter */
838 while (totlen >= 2) {
839 int len = elt->len;
840
841 /* validate remaining totlen */
842 if ((elt->id == key) && (totlen >= (len + 2)))
Jason Cooper90ea2292010-09-14 09:45:32 -0400843 return elt;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700844
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700845 elt = (bcm_tlv_t *) ((u8 *) elt + (len + 2));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700846 totlen -= (len + 2);
847 }
848
849 return NULL;
850}
851
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700852
853#if defined(BCMDBG)
854int
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700855bcm_format_flags(const bcm_bit_desc_t *bd, u32 flags, char *buf, int len)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700856{
857 int i;
858 char *p = buf;
859 char hexstr[16];
860 int slen = 0, nlen = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700861 u32 bit;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700862 const char *name;
863
864 if (len < 2 || !buf)
865 return 0;
866
867 buf[0] = '\0';
868
869 for (i = 0; flags != 0; i++) {
870 bit = bd[i].bit;
871 name = bd[i].name;
872 if (bit == 0 && flags != 0) {
873 /* print any unnamed bits */
874 snprintf(hexstr, 16, "0x%X", flags);
875 name = hexstr;
876 flags = 0; /* exit loop */
877 } else if ((flags & bit) == 0)
878 continue;
879 flags &= ~bit;
880 nlen = strlen(name);
881 slen += nlen;
882 /* count btwn flag space */
883 if (flags != 0)
884 slen += 1;
885 /* need NULL char as well */
886 if (len <= slen)
887 break;
888 /* copy NULL char but don't count it */
889 strncpy(p, name, nlen + 1);
890 p += nlen;
891 /* copy btwn flag space and NULL char */
892 if (flags != 0)
893 p += snprintf(p, 2, " ");
894 len -= slen;
895 }
896
897 /* indicate the str was too short */
898 if (flags != 0) {
899 if (len < 2)
900 p -= 2 - len; /* overwrite last char */
901 p += snprintf(p, 2, ">");
902 }
903
904 return (int)(p - buf);
905}
906
907/* print bytes formatted as hex to a string. return the resulting string length */
908int bcm_format_hex(char *str, const void *bytes, int len)
909{
910 int i;
911 char *p = str;
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700912 const u8 *src = (const u8 *)bytes;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700913
914 for (i = 0; i < len; i++) {
915 p += snprintf(p, 3, "%02X", *src);
916 src++;
917 }
918 return (int)(p - str);
919}
920#endif /* defined(BCMDBG) */
921
922/* pretty hex print a contiguous buffer */
Greg Kroah-Hartman580a0bd2010-10-05 11:09:48 -0700923void prhex(const char *msg, unsigned char *buf, uint nbytes)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700924{
925 char line[128], *p;
926 int len = sizeof(line);
927 int nchar;
928 uint i;
929
930 if (msg && (msg[0] != '\0'))
931 printf("%s:\n", msg);
932
933 p = line;
934 for (i = 0; i < nbytes; i++) {
935 if (i % 16 == 0) {
936 nchar = snprintf(p, len, " %04d: ", i); /* line prefix */
937 p += nchar;
938 len -= nchar;
939 }
940 if (len > 0) {
941 nchar = snprintf(p, len, "%02x ", buf[i]);
942 p += nchar;
943 len -= nchar;
944 }
945
946 if (i % 16 == 15) {
947 printf("%s\n", line); /* flush line */
948 p = line;
949 len = sizeof(line);
950 }
951 }
952
953 /* flush last partial line */
954 if (p != line)
955 printf("%s\n", line);
956}
957
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700958char *bcm_chipname(uint chipid, char *buf, uint len)
959{
960 const char *fmt;
961
962 fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
963 snprintf(buf, len, fmt, chipid);
964 return buf;
965}
966
nohee koea3b8a22010-10-09 10:34:38 -0700967uint bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)
968{
969 uint len;
970
971 len = strlen(name) + 1;
972
973 if ((len + datalen) > buflen)
974 return 0;
975
976 strncpy(buf, name, buflen);
977
978 /* append data onto the end of the name string */
979 memcpy(&buf[len], data, datalen);
980 len += datalen;
981
982 return len;
983}
984
985/* Quarter dBm units to mW
986 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
987 * Table is offset so the last entry is largest mW value that fits in
988 * a u16.
989 */
990
991#define QDBM_OFFSET 153 /* Offset for first entry */
992#define QDBM_TABLE_LEN 40 /* Table size */
993
994/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
995 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
996 */
997#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
998
999/* Largest mW value that will round down to the last table entry,
1000 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
1001 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
1002 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
1003 */
1004#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
1005
1006static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
1007/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
1008/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
1009/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
1010/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
1011/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
1012/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
1013};
1014
1015u16 bcm_qdbm_to_mw(u8 qdbm)
1016{
1017 uint factor = 1;
1018 int idx = qdbm - QDBM_OFFSET;
1019
1020 if (idx >= QDBM_TABLE_LEN) {
1021 /* clamp to max u16 mW value */
1022 return 0xFFFF;
1023 }
1024
1025 /* scale the qdBm index up to the range of the table 0-40
1026 * where an offset of 40 qdBm equals a factor of 10 mW.
1027 */
1028 while (idx < 0) {
1029 idx += 40;
1030 factor *= 10;
1031 }
1032
1033 /* return the mW value scaled down to the correct factor of 10,
1034 * adding in factor/2 to get proper rounding.
1035 */
1036 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
1037}
1038u8 bcm_mw_to_qdbm(u16 mw)
1039{
1040 u8 qdbm;
1041 int offset;
1042 uint mw_uint = mw;
1043 uint boundary;
1044
1045 /* handle boundary case */
1046 if (mw_uint <= 1)
1047 return 0;
1048
1049 offset = QDBM_OFFSET;
1050
1051 /* move mw into the range of the table */
1052 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
1053 mw_uint *= 10;
1054 offset -= 40;
1055 }
1056
1057 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
1058 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
1059 nqdBm_to_mW_map[qdbm]) / 2;
1060 if (mw_uint < boundary)
1061 break;
1062 }
1063
1064 qdbm += (u8) offset;
1065
1066 return qdbm;
1067}
1068uint bcm_bitcount(u8 *bitmap, uint length)
Jason Coopera2627bc2010-09-14 09:45:31 -04001069{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001070 uint bitcount = 0, i;
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07001071 u8 tmp;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001072 for (i = 0; i < length; i++) {
1073 tmp = bitmap[i];
1074 while (tmp) {
1075 bitcount++;
1076 tmp &= (tmp - 1);
1077 }
1078 }
1079 return bitcount;
1080}
nohee koea3b8a22010-10-09 10:34:38 -07001081/* Initialization of bcmstrbuf structure */
1082void bcm_binit(struct bcmstrbuf *b, char *buf, uint size)
1083{
1084 b->origsize = b->size = size;
1085 b->origbuf = b->buf = buf;
1086}
1087
1088/* Buffer sprintf wrapper to guard against buffer overflow */
1089int bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...)
1090{
1091 va_list ap;
1092 int r;
1093
1094 va_start(ap, fmt);
1095 r = vsnprintf(b->buf, b->size, fmt, ap);
1096
1097 /* Non Ansi C99 compliant returns -1,
1098 * Ansi compliant return r >= b->size,
1099 * bcmstdlib returns 0, handle all
1100 */
1101 if ((r == -1) || (r >= (int)b->size) || (r == 0)) {
1102 b->size = 0;
1103 } else {
1104 b->size -= r;
1105 b->buf += r;
1106 }
1107
1108 va_end(ap);
1109
1110 return r;
1111}
1112