blob: 64d1440a504140afc378743140e8d41932722b05 [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>
Brett Rudleyc6ac24e2010-10-26 11:55:23 -070022#include <linux/module.h>
23#include <linux/pci.h>
24#include <linux/netdevice.h>
Greg Kroah-Hartmana1c16ed2010-10-21 11:17:44 -070025#include <osl.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070026#include <bcmutils.h>
27#include <siutils.h>
28#include <bcmnvram.h>
29#include <bcmendian.h>
30#include <bcmdevs.h>
31#include <proto/ethernet.h>
32#include <proto/802.1d.h>
33#include <proto/802.11.h>
34
nohee koea3b8a22010-10-09 10:34:38 -070035/* copy a buffer into a pkt buffer chain */
Arend van Sprielc26b1372010-11-23 14:06:23 +010036uint pktfrombuf(struct osl_info *osh, struct sk_buff *p, uint offset, int len,
Brett Rudleye69284f2010-11-16 15:45:48 -080037 unsigned char *buf)
nohee koea3b8a22010-10-09 10:34:38 -070038{
39 uint n, ret = 0;
40
41 /* skip 'offset' bytes */
Arend van Spriel54991ad2010-11-23 14:06:24 +010042 for (; p && offset; p = p->next) {
43 if (offset < (uint) (p->len))
nohee koea3b8a22010-10-09 10:34:38 -070044 break;
Arend van Spriel54991ad2010-11-23 14:06:24 +010045 offset -= p->len;
nohee koea3b8a22010-10-09 10:34:38 -070046 }
47
48 if (!p)
49 return 0;
50
51 /* copy the data */
Arend van Spriel54991ad2010-11-23 14:06:24 +010052 for (; p && len; p = p->next) {
53 n = min((uint) (p->len) - offset, (uint) len);
54 bcopy(buf, p->data + offset, n);
nohee koea3b8a22010-10-09 10:34:38 -070055 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 */
Arend van Sprielc26b1372010-11-23 14:06:23 +010064uint BCMFASTPATH pkttotlen(struct osl_info *osh, struct sk_buff *p)
Henry Ptasinskia9533e72010-09-08 21:04:42 -070065{
66 uint total;
67
68 total = 0;
Arend van Spriel54991ad2010-11-23 14:06:24 +010069 for (; p; p = p->next)
70 total += p->len;
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 */
Arend van Sprielc26b1372010-11-23 14:06:23 +010078struct sk_buff *BCMFASTPATH pktq_penq(struct pktq *pq, int prec,
79 struct sk_buff *p)
Henry Ptasinskia9533e72010-09-08 21:04:42 -070080{
81 struct pktq_prec *q;
82
83 ASSERT(prec >= 0 && prec < pq->num_prec);
Arend van Spriel54991ad2010-11-23 14:06:24 +010084 ASSERT(p->prev == NULL); /* queueing chains not allowed */
Henry Ptasinskia9533e72010-09-08 21:04:42 -070085
86 ASSERT(!pktq_full(pq));
87 ASSERT(!pktq_pfull(pq, prec));
88
89 q = &pq->q[prec];
90
91 if (q->head)
Arend van Spriel54991ad2010-11-23 14:06:24 +010092 q->tail->prev = p;
Henry Ptasinskia9533e72010-09-08 21:04:42 -070093 else
94 q->head = p;
95
96 q->tail = p;
97 q->len++;
98
99 pq->len++;
100
101 if (pq->hi_prec < prec)
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700102 pq->hi_prec = (u8) prec;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700103
104 return p;
105}
106
Arend van Sprielc26b1372010-11-23 14:06:23 +0100107struct sk_buff *BCMFASTPATH pktq_penq_head(struct pktq *pq, int prec,
108 struct sk_buff *p)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700109{
110 struct pktq_prec *q;
111
112 ASSERT(prec >= 0 && prec < pq->num_prec);
Arend van Spriel54991ad2010-11-23 14:06:24 +0100113 ASSERT(p->prev == NULL); /* queueing chains not allowed */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700114
115 ASSERT(!pktq_full(pq));
116 ASSERT(!pktq_pfull(pq, prec));
117
118 q = &pq->q[prec];
119
120 if (q->head == NULL)
121 q->tail = p;
122
Arend van Spriel54991ad2010-11-23 14:06:24 +0100123 p->prev = q->head;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700124 q->head = p;
125 q->len++;
126
127 pq->len++;
128
129 if (pq->hi_prec < prec)
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700130 pq->hi_prec = (u8) prec;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700131
132 return p;
133}
134
Arend van Sprielc26b1372010-11-23 14:06:23 +0100135struct sk_buff *BCMFASTPATH pktq_pdeq(struct pktq *pq, int prec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700136{
137 struct pktq_prec *q;
Arend van Sprielc26b1372010-11-23 14:06:23 +0100138 struct sk_buff *p;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700139
140 ASSERT(prec >= 0 && prec < pq->num_prec);
141
142 q = &pq->q[prec];
143
Jason Cooperca8c1e52010-09-14 09:45:33 -0400144 p = q->head;
145 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700146 return NULL;
147
Arend van Spriel54991ad2010-11-23 14:06:24 +0100148 q->head = p->prev;
Jason Cooperca8c1e52010-09-14 09:45:33 -0400149 if (q->head == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700150 q->tail = NULL;
151
152 q->len--;
153
154 pq->len--;
155
Arend van Spriel54991ad2010-11-23 14:06:24 +0100156 p->prev = NULL;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700157
158 return p;
159}
160
Arend van Sprielc26b1372010-11-23 14:06:23 +0100161struct sk_buff *BCMFASTPATH pktq_pdeq_tail(struct pktq *pq, int prec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700162{
163 struct pktq_prec *q;
Arend van Sprielc26b1372010-11-23 14:06:23 +0100164 struct sk_buff *p, *prev;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700165
166 ASSERT(prec >= 0 && prec < pq->num_prec);
167
168 q = &pq->q[prec];
169
Jason Cooperca8c1e52010-09-14 09:45:33 -0400170 p = q->head;
171 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700172 return NULL;
173
Arend van Spriel54991ad2010-11-23 14:06:24 +0100174 for (prev = NULL; p != q->tail; p = p->prev)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700175 prev = p;
176
177 if (prev)
Arend van Spriel54991ad2010-11-23 14:06:24 +0100178 prev->prev = NULL;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700179 else
180 q->head = NULL;
181
182 q->tail = prev;
183 q->len--;
184
185 pq->len--;
186
187 return p;
188}
189
nohee koea3b8a22010-10-09 10:34:38 -0700190#ifdef BRCM_FULLMAC
Brett Rudleye69284f2010-11-16 15:45:48 -0800191void pktq_pflush(struct osl_info *osh, struct pktq *pq, int prec, bool dir)
nohee koea3b8a22010-10-09 10:34:38 -0700192{
193 struct pktq_prec *q;
Arend van Sprielc26b1372010-11-23 14:06:23 +0100194 struct sk_buff *p;
nohee koea3b8a22010-10-09 10:34:38 -0700195
196 q = &pq->q[prec];
197 p = q->head;
198 while (p) {
Arend van Spriel54991ad2010-11-23 14:06:24 +0100199 q->head = p->prev;
200 p->prev = NULL;
nohee koea3b8a22010-10-09 10:34:38 -0700201 PKTFREE(osh, p, dir);
202 q->len--;
203 pq->len--;
204 p = q->head;
205 }
206 ASSERT(q->len == 0);
207 q->tail = NULL;
208}
209
Brett Rudleye69284f2010-11-16 15:45:48 -0800210void pktq_flush(struct osl_info *osh, struct pktq *pq, bool dir)
nohee koea3b8a22010-10-09 10:34:38 -0700211{
212 int prec;
213 for (prec = 0; prec < pq->num_prec; prec++)
214 pktq_pflush(osh, pq, prec, dir);
215 ASSERT(pq->len == 0);
216}
217#else /* !BRCM_FULLMAC */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700218void
Brett Rudleye69284f2010-11-16 15:45:48 -0800219pktq_pflush(struct osl_info *osh, struct pktq *pq, int prec, bool dir,
220 ifpkt_cb_t fn, int arg)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700221{
222 struct pktq_prec *q;
Arend van Sprielc26b1372010-11-23 14:06:23 +0100223 struct sk_buff *p, *prev = NULL;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700224
225 q = &pq->q[prec];
226 p = q->head;
227 while (p) {
228 if (fn == NULL || (*fn) (p, arg)) {
229 bool head = (p == q->head);
230 if (head)
Arend van Spriel54991ad2010-11-23 14:06:24 +0100231 q->head = p->prev;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700232 else
Arend van Spriel54991ad2010-11-23 14:06:24 +0100233 prev->prev = p->prev;
234 p->prev = NULL;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700235 PKTFREE(osh, p, dir);
236 q->len--;
237 pq->len--;
Arend van Spriel54991ad2010-11-23 14:06:24 +0100238 p = (head ? q->head : prev->prev);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700239 } else {
240 prev = p;
Arend van Spriel54991ad2010-11-23 14:06:24 +0100241 p = p->prev;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700242 }
243 }
244
245 if (q->head == NULL) {
246 ASSERT(q->len == 0);
247 q->tail = NULL;
248 }
249}
250
Brett Rudleye69284f2010-11-16 15:45:48 -0800251void pktq_flush(struct osl_info *osh, struct pktq *pq, bool dir,
252 ifpkt_cb_t fn, int arg)
nohee koea3b8a22010-10-09 10:34:38 -0700253{
254 int prec;
255 for (prec = 0; prec < pq->num_prec; prec++)
256 pktq_pflush(osh, pq, prec, dir, fn, arg);
257 if (fn == NULL)
258 ASSERT(pq->len == 0);
259}
260#endif /* BRCM_FULLMAC */
261
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700262void pktq_init(struct pktq *pq, int num_prec, int max_len)
263{
264 int prec;
265
266 ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC);
267
268 /* pq is variable size; only zero out what's requested */
Brett Rudley9249ede2010-11-30 20:09:49 -0800269 memset(pq, 0,
Greg Kroah-Hartmance0f1b82010-10-08 11:44:45 -0700270 offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700271
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700272 pq->num_prec = (u16) num_prec;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700273
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700274 pq->max = (u16) max_len;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700275
276 for (prec = 0; prec < num_prec; prec++)
277 pq->q[prec].max = pq->max;
278}
279
Arend van Sprielc26b1372010-11-23 14:06:23 +0100280struct sk_buff *pktq_peek_tail(struct pktq *pq, int *prec_out)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700281{
282 int prec;
283
284 if (pq->len == 0)
285 return NULL;
286
287 for (prec = 0; prec < pq->hi_prec; prec++)
288 if (pq->q[prec].head)
289 break;
290
291 if (prec_out)
292 *prec_out = prec;
293
Jason Cooper90ea2292010-09-14 09:45:32 -0400294 return pq->q[prec].tail;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700295}
296
nohee koea3b8a22010-10-09 10:34:38 -0700297/* Return sum of lengths of a specific set of precedences */
298int pktq_mlen(struct pktq *pq, uint prec_bmp)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700299{
nohee koea3b8a22010-10-09 10:34:38 -0700300 int prec, len;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700301
nohee koea3b8a22010-10-09 10:34:38 -0700302 len = 0;
303
304 for (prec = 0; prec <= pq->hi_prec; prec++)
305 if (prec_bmp & (1 << prec))
306 len += pq->q[prec].len;
307
308 return len;
309}
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700310/* Priority dequeue from a specific set of precedences */
Arend van Sprielc26b1372010-11-23 14:06:23 +0100311struct sk_buff *BCMFASTPATH pktq_mdeq(struct pktq *pq, uint prec_bmp,
312 int *prec_out)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700313{
314 struct pktq_prec *q;
Arend van Sprielc26b1372010-11-23 14:06:23 +0100315 struct sk_buff *p;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700316 int prec;
317
318 if (pq->len == 0)
319 return NULL;
320
321 while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
322 pq->hi_prec--;
323
324 while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)
325 if (prec-- == 0)
326 return NULL;
327
328 q = &pq->q[prec];
329
Jason Cooperca8c1e52010-09-14 09:45:33 -0400330 p = q->head;
331 if (p == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700332 return NULL;
333
Arend van Spriel54991ad2010-11-23 14:06:24 +0100334 q->head = p->prev;
Jason Cooperca8c1e52010-09-14 09:45:33 -0400335 if (q->head == NULL)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700336 q->tail = NULL;
337
338 q->len--;
339
340 if (prec_out)
341 *prec_out = prec;
342
343 pq->len--;
344
Arend van Spriel54991ad2010-11-23 14:06:24 +0100345 p->prev = NULL;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700346
347 return p;
348}
349
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700350/* parse a xx:xx:xx:xx:xx:xx format ethernet address */
nohee koea3b8a22010-10-09 10:34:38 -0700351int bcm_ether_atoe(char *p, struct ether_addr *ea)
Jason Coopera2627bc2010-09-14 09:45:31 -0400352{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700353 int i = 0;
354
355 for (;;) {
Andy Shevchenko48c51a82010-09-15 12:47:18 +0300356 ea->octet[i++] = (char)simple_strtoul(p, &p, 16);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700357 if (!*p++ || i == 6)
358 break;
359 }
360
Jason Cooper90ea2292010-09-14 09:45:32 -0400361 return i == 6;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700362}
363
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700364/*
365 * Search the name=value vars for a specific one and return its value.
366 * Returns NULL if not found.
367 */
368char *getvar(char *vars, const char *name)
369{
370 char *s;
371 int len;
372
373 if (!name)
374 return NULL;
375
376 len = strlen(name);
377 if (len == 0)
378 return NULL;
379
380 /* first look in vars[] */
381 for (s = vars; s && *s;) {
382 if ((bcmp(s, name, len) == 0) && (s[len] == '='))
Jason Cooper90ea2292010-09-14 09:45:32 -0400383 return &s[len + 1];
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700384
Jason Cooper62145822010-09-14 09:45:34 -0400385 while (*s++)
386 ;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700387 }
nohee koea3b8a22010-10-09 10:34:38 -0700388#ifdef BRCM_FULLMAC
389 return NULL;
390#else
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700391 /* then query nvram */
Jason Cooper90ea2292010-09-14 09:45:32 -0400392 return nvram_get(name);
nohee koea3b8a22010-10-09 10:34:38 -0700393#endif
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700394}
395
396/*
397 * Search the vars for a specific one and return its value as
398 * an integer. Returns 0 if not found.
399 */
400int getintvar(char *vars, const char *name)
401{
402 char *val;
403
Jason Cooperca8c1e52010-09-14 09:45:33 -0400404 val = getvar(vars, name);
405 if (val == NULL)
Jason Cooper90ea2292010-09-14 09:45:32 -0400406 return 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700407
Andy Shevchenko48c51a82010-09-15 12:47:18 +0300408 return simple_strtoul(val, NULL, 0);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700409}
410
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700411#if defined(BCMDBG)
412/* pretty hex print a pkt buffer chain */
Arend van Sprielc26b1372010-11-23 14:06:23 +0100413void prpkt(const char *msg, struct osl_info *osh, struct sk_buff *p0)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700414{
Arend van Sprielc26b1372010-11-23 14:06:23 +0100415 struct sk_buff *p;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700416
417 if (msg && (msg[0] != '\0'))
418 printf("%s:\n", msg);
419
Arend van Spriel54991ad2010-11-23 14:06:24 +0100420 for (p = p0; p; p = p->next)
421 prhex(NULL, p->data, p->len);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700422}
423#endif /* defined(BCMDBG) */
424
nohee koea3b8a22010-10-09 10:34:38 -0700425static char bcm_undeferrstr[BCME_STRLEN];
426
427static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE;
428
429/* Convert the error codes into related error strings */
430const char *bcmerrorstr(int bcmerror)
431{
432 /* check if someone added a bcmerror code but
433 forgot to add errorstring */
434 ASSERT(ABS(BCME_LAST) == (ARRAY_SIZE(bcmerrorstrtable) - 1));
435
436 if (bcmerror > 0 || bcmerror < BCME_LAST) {
437 snprintf(bcm_undeferrstr, BCME_STRLEN, "Undefined error %d",
438 bcmerror);
439 return bcm_undeferrstr;
440 }
441
442 ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN);
443
444 return bcmerrorstrtable[-bcmerror];
445}
446
447/* iovar table lookup */
448const bcm_iovar_t *bcm_iovar_lookup(const bcm_iovar_t *table, const char *name)
449{
450 const bcm_iovar_t *vi;
451 const char *lookup_name;
452
453 /* skip any ':' delimited option prefixes */
454 lookup_name = strrchr(name, ':');
455 if (lookup_name != NULL)
456 lookup_name++;
457 else
458 lookup_name = name;
459
460 ASSERT(table != NULL);
461
462 for (vi = table; vi->name; vi++) {
463 if (!strcmp(vi->name, lookup_name))
464 return vi;
465 }
466 /* ran to end of table */
467
468 return NULL; /* var name not found */
469}
470
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400471int bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700472{
473 int bcmerror = 0;
474
475 /* length check on io buf */
476 switch (vi->type) {
477 case IOVT_BOOL:
478 case IOVT_INT8:
479 case IOVT_INT16:
480 case IOVT_INT32:
481 case IOVT_UINT8:
482 case IOVT_UINT16:
483 case IOVT_UINT32:
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700484 /* all integers are s32 sized args at the ioctl interface */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700485 if (len < (int)sizeof(int)) {
486 bcmerror = BCME_BUFTOOSHORT;
487 }
488 break;
489
490 case IOVT_BUFFER:
491 /* buffer must meet minimum length requirement */
492 if (len < vi->minlen) {
493 bcmerror = BCME_BUFTOOSHORT;
494 }
495 break;
496
497 case IOVT_VOID:
498 if (!set) {
499 /* Cannot return nil... */
500 bcmerror = BCME_UNSUPPORTED;
501 } else if (len) {
502 /* Set is an action w/o parameters */
503 bcmerror = BCME_BUFTOOLONG;
504 }
505 break;
506
507 default:
508 /* unknown type for length check in iovar info */
509 ASSERT(0);
510 bcmerror = BCME_UNSUPPORTED;
511 }
512
513 return bcmerror;
514}
515
516/*******************************************************************************
517 * crc8
518 *
519 * Computes a crc8 over the input data using the polynomial:
520 *
521 * x^8 + x^7 +x^6 + x^4 + x^2 + 1
522 *
523 * The caller provides the initial value (either CRC8_INIT_VALUE
524 * or the previous returned value) to allow for processing of
525 * discontiguous blocks of data. When generating the CRC the
526 * caller is responsible for complementing the final return value
527 * and inserting it into the byte stream. When checking, a final
528 * return value of CRC8_GOOD_VALUE indicates a valid CRC.
529 *
530 * Reference: Dallas Semiconductor Application Note 27
531 * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
532 * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
533 * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
534 *
535 * ****************************************************************************
536 */
537
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700538static const u8 crc8_table[256] = {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700539 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
540 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
541 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
542 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
543 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
544 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
545 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
546 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
547 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
548 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
549 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
550 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
551 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
552 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
553 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
554 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
555 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
556 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
557 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
558 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
559 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
560 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
561 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
562 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
563 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
564 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
565 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
566 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
567 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
568 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
569 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
570 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F
571};
572
573#define CRC_INNER_LOOP(n, c, x) \
Jason Cooper0d706ef2010-09-14 09:45:39 -0400574 ((c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff])
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700575
nohee koea3b8a22010-10-09 10:34:38 -0700576u8 hndcrc8(u8 *pdata, /* pointer to array of data to process */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700577 uint nbytes, /* number of input data bytes to process */
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700578 u8 crc /* either CRC8_INIT_VALUE or previous return value */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700579 ) {
580 /* hard code the crc loop instead of using CRC_INNER_LOOP macro
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700581 * to avoid the undefined and unnecessary (u8 >> 8) operation.
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700582 */
583 while (nbytes-- > 0)
584 crc = crc8_table[(crc ^ *pdata++) & 0xff];
585
586 return crc;
587}
588
589/*******************************************************************************
590 * crc16
591 *
592 * Computes a crc16 over the input data using the polynomial:
593 *
594 * x^16 + x^12 +x^5 + 1
595 *
596 * The caller provides the initial value (either CRC16_INIT_VALUE
597 * or the previous returned value) to allow for processing of
598 * discontiguous blocks of data. When generating the CRC the
599 * caller is responsible for complementing the final return value
600 * and inserting it into the byte stream. When checking, a final
601 * return value of CRC16_GOOD_VALUE indicates a valid CRC.
602 *
603 * Reference: Dallas Semiconductor Application Note 27
604 * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
605 * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
606 * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
607 *
608 * ****************************************************************************
609 */
610
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700611static const u16 crc16_table[256] = {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700612 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
613 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
614 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
615 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
616 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
617 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
618 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
619 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
620 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
621 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
622 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
623 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
624 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
625 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
626 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
627 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
628 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
629 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
630 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
631 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
632 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
633 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
634 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
635 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
636 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
637 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
638 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
639 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
640 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
641 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
642 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
643 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
644};
645
nohee koea3b8a22010-10-09 10:34:38 -0700646u16 hndcrc16(u8 *pdata, /* pointer to array of data to process */
647 uint nbytes, /* number of input data bytes to process */
648 u16 crc /* either CRC16_INIT_VALUE or previous return value */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700649 ) {
650 while (nbytes-- > 0)
651 CRC_INNER_LOOP(16, crc, *pdata++);
652 return crc;
653}
654
nohee koea3b8a22010-10-09 10:34:38 -0700655static const u32 crc32_table[256] = {
656 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
657 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
658 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
659 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
660 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
661 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
662 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
663 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
664 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
665 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
666 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
667 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
668 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
669 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
670 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
671 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
672 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
673 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
674 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
675 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
676 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
677 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
678 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
679 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
680 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
681 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
682 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
683 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
684 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
685 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
686 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
687 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
688 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
689 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
690 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
691 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
692 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
693 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
694 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
695 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
696 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
697 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
698 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
699 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
700 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
701 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
702 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
703 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
704 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
705 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
706 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
707 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
708 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
709 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
710 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
711 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
712 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
713 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
714 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
715 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
716 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
717 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
718 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
719 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
720};
721
722u32 hndcrc32(u8 *pdata, /* pointer to array of data to process */
723 uint nbytes, /* number of input data bytes to process */
724 u32 crc /* either CRC32_INIT_VALUE or previous
725 return value */
726)
727{
728 u8 *pend;
729#ifdef __mips__
730 u8 tmp[4];
731 unsigned long *tptr = (unsigned long *) tmp;
732
733 /* in case the beginning of the buffer isn't aligned */
734 pend = (u8 *) ((uint) (pdata + 3) & 0xfffffffc);
735 nbytes -= (pend - pdata);
736 while (pdata < pend)
737 CRC_INNER_LOOP(32, crc, *pdata++);
738
739 /* handle bulk of data as 32-bit words */
740 pend = pdata + (nbytes & 0xfffffffc);
741 while (pdata < pend) {
742 *tptr = *(unsigned long *) pdata;
743 pdata += sizeof(unsigned long *);
744 CRC_INNER_LOOP(32, crc, tmp[0]);
745 CRC_INNER_LOOP(32, crc, tmp[1]);
746 CRC_INNER_LOOP(32, crc, tmp[2]);
747 CRC_INNER_LOOP(32, crc, tmp[3]);
748 }
749
750 /* 1-3 bytes at end of buffer */
751 pend = pdata + (nbytes & 0x03);
752 while (pdata < pend)
753 CRC_INNER_LOOP(32, crc, *pdata++);
754#else
755 pend = pdata + nbytes;
756 while (pdata < pend)
757 CRC_INNER_LOOP(32, crc, *pdata++);
758#endif /* __mips__ */
759
760 return crc;
761}
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700762/*
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700763 * Traverse a string of 1-byte tag/1-byte length/variable-length value
764 * triples, returning a pointer to the substring whose first element
765 * matches tag
766 */
nohee koea3b8a22010-10-09 10:34:38 -0700767bcm_tlv_t *bcm_parse_tlvs(void *buf, int buflen, uint key)
Jason Coopera2627bc2010-09-14 09:45:31 -0400768{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700769 bcm_tlv_t *elt;
770 int totlen;
771
772 elt = (bcm_tlv_t *) buf;
773 totlen = buflen;
774
775 /* find tagged parameter */
776 while (totlen >= 2) {
777 int len = elt->len;
778
779 /* validate remaining totlen */
780 if ((elt->id == key) && (totlen >= (len + 2)))
Jason Cooper90ea2292010-09-14 09:45:32 -0400781 return elt;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700782
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700783 elt = (bcm_tlv_t *) ((u8 *) elt + (len + 2));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700784 totlen -= (len + 2);
785 }
786
787 return NULL;
788}
789
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700790
791#if defined(BCMDBG)
792int
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700793bcm_format_flags(const bcm_bit_desc_t *bd, u32 flags, char *buf, int len)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700794{
795 int i;
796 char *p = buf;
797 char hexstr[16];
798 int slen = 0, nlen = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700799 u32 bit;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700800 const char *name;
801
802 if (len < 2 || !buf)
803 return 0;
804
805 buf[0] = '\0';
806
807 for (i = 0; flags != 0; i++) {
808 bit = bd[i].bit;
809 name = bd[i].name;
810 if (bit == 0 && flags != 0) {
811 /* print any unnamed bits */
812 snprintf(hexstr, 16, "0x%X", flags);
813 name = hexstr;
814 flags = 0; /* exit loop */
815 } else if ((flags & bit) == 0)
816 continue;
817 flags &= ~bit;
818 nlen = strlen(name);
819 slen += nlen;
820 /* count btwn flag space */
821 if (flags != 0)
822 slen += 1;
823 /* need NULL char as well */
824 if (len <= slen)
825 break;
826 /* copy NULL char but don't count it */
827 strncpy(p, name, nlen + 1);
828 p += nlen;
829 /* copy btwn flag space and NULL char */
830 if (flags != 0)
831 p += snprintf(p, 2, " ");
832 len -= slen;
833 }
834
835 /* indicate the str was too short */
836 if (flags != 0) {
837 if (len < 2)
838 p -= 2 - len; /* overwrite last char */
839 p += snprintf(p, 2, ">");
840 }
841
842 return (int)(p - buf);
843}
844
845/* print bytes formatted as hex to a string. return the resulting string length */
846int bcm_format_hex(char *str, const void *bytes, int len)
847{
848 int i;
849 char *p = str;
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700850 const u8 *src = (const u8 *)bytes;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700851
852 for (i = 0; i < len; i++) {
853 p += snprintf(p, 3, "%02X", *src);
854 src++;
855 }
856 return (int)(p - str);
857}
858#endif /* defined(BCMDBG) */
859
860/* pretty hex print a contiguous buffer */
Greg Kroah-Hartman580a0bd2010-10-05 11:09:48 -0700861void prhex(const char *msg, unsigned char *buf, uint nbytes)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700862{
863 char line[128], *p;
864 int len = sizeof(line);
865 int nchar;
866 uint i;
867
868 if (msg && (msg[0] != '\0'))
869 printf("%s:\n", msg);
870
871 p = line;
872 for (i = 0; i < nbytes; i++) {
873 if (i % 16 == 0) {
874 nchar = snprintf(p, len, " %04d: ", i); /* line prefix */
875 p += nchar;
876 len -= nchar;
877 }
878 if (len > 0) {
879 nchar = snprintf(p, len, "%02x ", buf[i]);
880 p += nchar;
881 len -= nchar;
882 }
883
884 if (i % 16 == 15) {
885 printf("%s\n", line); /* flush line */
886 p = line;
887 len = sizeof(line);
888 }
889 }
890
891 /* flush last partial line */
892 if (p != line)
893 printf("%s\n", line);
894}
895
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700896char *bcm_chipname(uint chipid, char *buf, uint len)
897{
898 const char *fmt;
899
900 fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
901 snprintf(buf, len, fmt, chipid);
902 return buf;
903}
904
nohee koea3b8a22010-10-09 10:34:38 -0700905uint bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)
906{
907 uint len;
908
909 len = strlen(name) + 1;
910
911 if ((len + datalen) > buflen)
912 return 0;
913
914 strncpy(buf, name, buflen);
915
916 /* append data onto the end of the name string */
917 memcpy(&buf[len], data, datalen);
918 len += datalen;
919
920 return len;
921}
922
923/* Quarter dBm units to mW
924 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
925 * Table is offset so the last entry is largest mW value that fits in
926 * a u16.
927 */
928
929#define QDBM_OFFSET 153 /* Offset for first entry */
930#define QDBM_TABLE_LEN 40 /* Table size */
931
932/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
933 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
934 */
935#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
936
937/* Largest mW value that will round down to the last table entry,
938 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
939 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
940 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
941 */
942#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
943
944static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
945/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
946/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
947/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
948/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
949/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
950/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
951};
952
953u16 bcm_qdbm_to_mw(u8 qdbm)
954{
955 uint factor = 1;
956 int idx = qdbm - QDBM_OFFSET;
957
958 if (idx >= QDBM_TABLE_LEN) {
959 /* clamp to max u16 mW value */
960 return 0xFFFF;
961 }
962
963 /* scale the qdBm index up to the range of the table 0-40
964 * where an offset of 40 qdBm equals a factor of 10 mW.
965 */
966 while (idx < 0) {
967 idx += 40;
968 factor *= 10;
969 }
970
971 /* return the mW value scaled down to the correct factor of 10,
972 * adding in factor/2 to get proper rounding.
973 */
974 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
975}
976u8 bcm_mw_to_qdbm(u16 mw)
977{
978 u8 qdbm;
979 int offset;
980 uint mw_uint = mw;
981 uint boundary;
982
983 /* handle boundary case */
984 if (mw_uint <= 1)
985 return 0;
986
987 offset = QDBM_OFFSET;
988
989 /* move mw into the range of the table */
990 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
991 mw_uint *= 10;
992 offset -= 40;
993 }
994
995 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
996 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
997 nqdBm_to_mW_map[qdbm]) / 2;
998 if (mw_uint < boundary)
999 break;
1000 }
1001
1002 qdbm += (u8) offset;
1003
1004 return qdbm;
1005}
1006uint bcm_bitcount(u8 *bitmap, uint length)
Jason Coopera2627bc2010-09-14 09:45:31 -04001007{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001008 uint bitcount = 0, i;
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07001009 u8 tmp;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001010 for (i = 0; i < length; i++) {
1011 tmp = bitmap[i];
1012 while (tmp) {
1013 bitcount++;
1014 tmp &= (tmp - 1);
1015 }
1016 }
1017 return bitcount;
1018}
nohee koea3b8a22010-10-09 10:34:38 -07001019/* Initialization of bcmstrbuf structure */
1020void bcm_binit(struct bcmstrbuf *b, char *buf, uint size)
1021{
1022 b->origsize = b->size = size;
1023 b->origbuf = b->buf = buf;
1024}
1025
1026/* Buffer sprintf wrapper to guard against buffer overflow */
1027int bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...)
1028{
1029 va_list ap;
1030 int r;
1031
1032 va_start(ap, fmt);
1033 r = vsnprintf(b->buf, b->size, fmt, ap);
1034
1035 /* Non Ansi C99 compliant returns -1,
1036 * Ansi compliant return r >= b->size,
1037 * bcmstdlib returns 0, handle all
1038 */
1039 if ((r == -1) || (r >= (int)b->size) || (r == 0)) {
1040 b->size = 0;
1041 } else {
1042 b->size -= r;
1043 b->buf += r;
1044 }
1045
1046 va_end(ap);
1047
1048 return r;
1049}
1050