aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/brcm80211/brcmutil
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/brcm80211/brcmutil')
-rw-r--r--drivers/staging/brcm80211/brcmutil/Makefile29
-rw-r--r--drivers/staging/brcm80211/brcmutil/utils.c787
-rw-r--r--drivers/staging/brcm80211/brcmutil/wifi.c131
3 files changed, 947 insertions, 0 deletions
diff --git a/drivers/staging/brcm80211/brcmutil/Makefile b/drivers/staging/brcm80211/brcmutil/Makefile
new file mode 100644
index 00000000000..6403423c021
--- /dev/null
+++ b/drivers/staging/brcm80211/brcmutil/Makefile
@@ -0,0 +1,29 @@
1#
2# Makefile fragment for Broadcom 802.11n Networking Device Driver Utilities
3#
4# Copyright (c) 2011 Broadcom Corporation
5#
6# Permission to use, copy, modify, and/or distribute this software for any
7# purpose with or without fee is hereby granted, provided that the above
8# copyright notice and this permission notice appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18ccflags-y := \
19 -Idrivers/staging/brcm80211/brcmutil \
20 -Idrivers/staging/brcm80211/include
21
22BRCMUTIL_OFILES := \
23 utils.o \
24 wifi.o
25
26MODULEPFX := brcmutil
27
28obj-$(CONFIG_BRCMUTIL) += $(MODULEPFX).o
29$(MODULEPFX)-objs = $(BRCMUTIL_OFILES)
diff --git a/drivers/staging/brcm80211/brcmutil/utils.c b/drivers/staging/brcm80211/brcmutil/utils.c
new file mode 100644
index 00000000000..37b6b779779
--- /dev/null
+++ b/drivers/staging/brcm80211/brcmutil/utils.c
@@ -0,0 +1,787 @@
1/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <linux/netdevice.h>
18#include <brcmu_utils.h>
19
20MODULE_AUTHOR("Broadcom Corporation");
21MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities.");
22MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
23MODULE_LICENSE("Dual BSD/GPL");
24
25struct sk_buff *brcmu_pkt_buf_get_skb(uint len)
26{
27 struct sk_buff *skb;
28
29 skb = dev_alloc_skb(len);
30 if (skb) {
31 skb_put(skb, len);
32 skb->priority = 0;
33 }
34
35 return skb;
36}
37EXPORT_SYMBOL(brcmu_pkt_buf_get_skb);
38
39/* Free the driver packet. Free the tag if present */
40void brcmu_pkt_buf_free_skb(struct sk_buff *skb)
41{
42 struct sk_buff *nskb;
43 int nest = 0;
44
45 /* perversion: we use skb->next to chain multi-skb packets */
46 while (skb) {
47 nskb = skb->next;
48 skb->next = NULL;
49
50 if (skb->destructor)
51 /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if
52 * destructor exists
53 */
54 dev_kfree_skb_any(skb);
55 else
56 /* can free immediately (even in_irq()) if destructor
57 * does not exist
58 */
59 dev_kfree_skb(skb);
60
61 nest++;
62 skb = nskb;
63 }
64}
65EXPORT_SYMBOL(brcmu_pkt_buf_free_skb);
66
67
68/* copy a buffer into a pkt buffer chain */
69uint brcmu_pktfrombuf(struct sk_buff *p, uint offset, int len,
70 unsigned char *buf)
71{
72 uint n, ret = 0;
73
74 /* skip 'offset' bytes */
75 for (; p && offset; p = p->next) {
76 if (offset < (uint) (p->len))
77 break;
78 offset -= p->len;
79 }
80
81 if (!p)
82 return 0;
83
84 /* copy the data */
85 for (; p && len; p = p->next) {
86 n = min((uint) (p->len) - offset, (uint) len);
87 memcpy(p->data + offset, buf, n);
88 buf += n;
89 len -= n;
90 ret += n;
91 offset = 0;
92 }
93
94 return ret;
95}
96EXPORT_SYMBOL(brcmu_pktfrombuf);
97
98/* return total length of buffer chain */
99uint brcmu_pkttotlen(struct sk_buff *p)
100{
101 uint total;
102
103 total = 0;
104 for (; p; p = p->next)
105 total += p->len;
106 return total;
107}
108EXPORT_SYMBOL(brcmu_pkttotlen);
109
110/*
111 * osl multiple-precedence packet queue
112 * hi_prec is always >= the number of the highest non-empty precedence
113 */
114struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec,
115 struct sk_buff *p)
116{
117 struct pktq_prec *q;
118
119 if (pktq_full(pq) || pktq_pfull(pq, prec))
120 return NULL;
121
122 q = &pq->q[prec];
123
124 if (q->head)
125 q->tail->prev = p;
126 else
127 q->head = p;
128
129 q->tail = p;
130 q->len++;
131
132 pq->len++;
133
134 if (pq->hi_prec < prec)
135 pq->hi_prec = (u8) prec;
136
137 return p;
138}
139EXPORT_SYMBOL(brcmu_pktq_penq);
140
141struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
142 struct sk_buff *p)
143{
144 struct pktq_prec *q;
145
146 if (pktq_full(pq) || pktq_pfull(pq, prec))
147 return NULL;
148
149 q = &pq->q[prec];
150
151 if (q->head == NULL)
152 q->tail = p;
153
154 p->prev = q->head;
155 q->head = p;
156 q->len++;
157
158 pq->len++;
159
160 if (pq->hi_prec < prec)
161 pq->hi_prec = (u8) prec;
162
163 return p;
164}
165EXPORT_SYMBOL(brcmu_pktq_penq_head);
166
167struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec)
168{
169 struct pktq_prec *q;
170 struct sk_buff *p;
171
172 q = &pq->q[prec];
173
174 p = q->head;
175 if (p == NULL)
176 return NULL;
177
178 q->head = p->prev;
179 if (q->head == NULL)
180 q->tail = NULL;
181
182 q->len--;
183
184 pq->len--;
185
186 p->prev = NULL;
187
188 return p;
189}
190EXPORT_SYMBOL(brcmu_pktq_pdeq);
191
192struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec)
193{
194 struct pktq_prec *q;
195 struct sk_buff *p, *prev;
196
197 q = &pq->q[prec];
198
199 p = q->head;
200 if (p == NULL)
201 return NULL;
202
203 for (prev = NULL; p != q->tail; p = p->prev)
204 prev = p;
205
206 if (prev)
207 prev->prev = NULL;
208 else
209 q->head = NULL;
210
211 q->tail = prev;
212 q->len--;
213
214 pq->len--;
215
216 return p;
217}
218EXPORT_SYMBOL(brcmu_pktq_pdeq_tail);
219
220void
221brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir,
222 ifpkt_cb_t fn, void *arg)
223{
224 struct pktq_prec *q;
225 struct sk_buff *p, *prev = NULL;
226
227 q = &pq->q[prec];
228 p = q->head;
229 while (p) {
230 if (fn == NULL || (*fn) (p, arg)) {
231 bool head = (p == q->head);
232 if (head)
233 q->head = p->prev;
234 else
235 prev->prev = p->prev;
236 p->prev = NULL;
237 brcmu_pkt_buf_free_skb(p);
238 q->len--;
239 pq->len--;
240 p = (head ? q->head : prev->prev);
241 } else {
242 prev = p;
243 p = p->prev;
244 }
245 }
246
247 if (q->head == NULL) {
248 q->tail = NULL;
249 }
250}
251EXPORT_SYMBOL(brcmu_pktq_pflush);
252
253void brcmu_pktq_flush(struct pktq *pq, bool dir,
254 ifpkt_cb_t fn, void *arg)
255{
256 int prec;
257 for (prec = 0; prec < pq->num_prec; prec++)
258 brcmu_pktq_pflush(pq, prec, dir, fn, arg);
259}
260EXPORT_SYMBOL(brcmu_pktq_flush);
261
262void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len)
263{
264 int prec;
265
266 /* pq is variable size; only zero out what's requested */
267 memset(pq, 0,
268 offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
269
270 pq->num_prec = (u16) num_prec;
271
272 pq->max = (u16) max_len;
273
274 for (prec = 0; prec < num_prec; prec++)
275 pq->q[prec].max = pq->max;
276}
277EXPORT_SYMBOL(brcmu_pktq_init);
278
279struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out)
280{
281 int prec;
282
283 if (pq->len == 0)
284 return NULL;
285
286 for (prec = 0; prec < pq->hi_prec; prec++)
287 if (pq->q[prec].head)
288 break;
289
290 if (prec_out)
291 *prec_out = prec;
292
293 return pq->q[prec].tail;
294}
295EXPORT_SYMBOL(brcmu_pktq_peek_tail);
296
297/* Return sum of lengths of a specific set of precedences */
298int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp)
299{
300 int prec, len;
301
302 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}
310EXPORT_SYMBOL(brcmu_pktq_mlen);
311
312/* Priority dequeue from a specific set of precedences */
313struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp,
314 int *prec_out)
315{
316 struct pktq_prec *q;
317 struct sk_buff *p;
318 int prec;
319
320 if (pq->len == 0)
321 return NULL;
322
323 while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
324 pq->hi_prec--;
325
326 while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)
327 if (prec-- == 0)
328 return NULL;
329
330 q = &pq->q[prec];
331
332 p = q->head;
333 if (p == NULL)
334 return NULL;
335
336 q->head = p->prev;
337 if (q->head == NULL)
338 q->tail = NULL;
339
340 q->len--;
341
342 if (prec_out)
343 *prec_out = prec;
344
345 pq->len--;
346
347 p->prev = NULL;
348
349 return p;
350}
351EXPORT_SYMBOL(brcmu_pktq_mdeq);
352
353/* parse a xx:xx:xx:xx:xx:xx format ethernet address */
354int brcmu_ether_atoe(char *p, u8 *ea)
355{
356 int i = 0;
357
358 for (;;) {
359 ea[i++] = (char)simple_strtoul(p, &p, 16);
360 if (!*p++ || i == 6)
361 break;
362 }
363
364 return i == 6;
365}
366EXPORT_SYMBOL(brcmu_ether_atoe);
367
368#if defined(BCMDBG)
369/* pretty hex print a pkt buffer chain */
370void brcmu_prpkt(const char *msg, struct sk_buff *p0)
371{
372 struct sk_buff *p;
373
374 if (msg && (msg[0] != '\0'))
375 printk(KERN_DEBUG "%s:\n", msg);
376
377 for (p = p0; p; p = p->next)
378 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len);
379}
380EXPORT_SYMBOL(brcmu_prpkt);
381#endif /* defined(BCMDBG) */
382
383/* iovar table lookup */
384const struct brcmu_iovar *brcmu_iovar_lookup(const struct brcmu_iovar *table,
385 const char *name)
386{
387 const struct brcmu_iovar *vi;
388 const char *lookup_name;
389
390 /* skip any ':' delimited option prefixes */
391 lookup_name = strrchr(name, ':');
392 if (lookup_name != NULL)
393 lookup_name++;
394 else
395 lookup_name = name;
396
397 for (vi = table; vi->name; vi++) {
398 if (!strcmp(vi->name, lookup_name))
399 return vi;
400 }
401 /* ran to end of table */
402
403 return NULL; /* var name not found */
404}
405EXPORT_SYMBOL(brcmu_iovar_lookup);
406
407int brcmu_iovar_lencheck(const struct brcmu_iovar *vi, void *arg, int len,
408 bool set)
409{
410 int bcmerror = 0;
411
412 /* length check on io buf */
413 switch (vi->type) {
414 case IOVT_BOOL:
415 case IOVT_INT8:
416 case IOVT_INT16:
417 case IOVT_INT32:
418 case IOVT_UINT8:
419 case IOVT_UINT16:
420 case IOVT_UINT32:
421 /* all integers are s32 sized args at the ioctl interface */
422 if (len < (int)sizeof(int)) {
423 bcmerror = -EOVERFLOW;
424 }
425 break;
426
427 case IOVT_BUFFER:
428 /* buffer must meet minimum length requirement */
429 if (len < vi->minlen) {
430 bcmerror = -EOVERFLOW;
431 }
432 break;
433
434 case IOVT_VOID:
435 if (!set) {
436 /* Cannot return nil... */
437 bcmerror = -ENOTSUPP;
438 } else if (len) {
439 /* Set is an action w/o parameters */
440 bcmerror = -ENOBUFS;
441 }
442 break;
443
444 default:
445 /* unknown type for length check in iovar info */
446 bcmerror = -ENOTSUPP;
447 }
448
449 return bcmerror;
450}
451EXPORT_SYMBOL(brcmu_iovar_lencheck);
452
453/*******************************************************************************
454 * crc8
455 *
456 * Computes a crc8 over the input data using the polynomial:
457 *
458 * x^8 + x^7 +x^6 + x^4 + x^2 + 1
459 *
460 * The caller provides the initial value (either CRC8_INIT_VALUE
461 * or the previous returned value) to allow for processing of
462 * discontiguous blocks of data. When generating the CRC the
463 * caller is responsible for complementing the final return value
464 * and inserting it into the byte stream. When checking, a final
465 * return value of CRC8_GOOD_VALUE indicates a valid CRC.
466 *
467 * Reference: Dallas Semiconductor Application Note 27
468 * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
469 * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
470 * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
471 *
472 * ****************************************************************************
473 */
474
475static const u8 crc8_table[256] = {
476 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
477 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
478 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
479 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
480 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
481 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
482 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
483 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
484 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
485 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
486 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
487 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
488 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
489 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
490 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
491 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
492 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
493 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
494 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
495 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
496 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
497 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
498 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
499 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
500 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
501 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
502 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
503 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
504 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
505 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
506 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
507 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F
508};
509
510u8 brcmu_crc8(u8 *pdata, /* pointer to array of data to process */
511 uint nbytes, /* number of input data bytes to process */
512 u8 crc /* either CRC8_INIT_VALUE or previous return value */
513 ) {
514 /* loop over the buffer data */
515 while (nbytes-- > 0)
516 crc = crc8_table[(crc ^ *pdata++) & 0xff];
517
518 return crc;
519}
520EXPORT_SYMBOL(brcmu_crc8);
521
522/*
523 * Traverse a string of 1-byte tag/1-byte length/variable-length value
524 * triples, returning a pointer to the substring whose first element
525 * matches tag
526 */
527struct brcmu_tlv *brcmu_parse_tlvs(void *buf, int buflen, uint key)
528{
529 struct brcmu_tlv *elt;
530 int totlen;
531
532 elt = (struct brcmu_tlv *) buf;
533 totlen = buflen;
534
535 /* find tagged parameter */
536 while (totlen >= 2) {
537 int len = elt->len;
538
539 /* validate remaining totlen */
540 if ((elt->id == key) && (totlen >= (len + 2)))
541 return elt;
542
543 elt = (struct brcmu_tlv *) ((u8 *) elt + (len + 2));
544 totlen -= (len + 2);
545 }
546
547 return NULL;
548}
549EXPORT_SYMBOL(brcmu_parse_tlvs);
550
551
552#if defined(BCMDBG)
553int
554brcmu_format_flags(const struct brcmu_bit_desc *bd, u32 flags, char *buf,
555 int len)
556{
557 int i;
558 char *p = buf;
559 char hexstr[16];
560 int slen = 0, nlen = 0;
561 u32 bit;
562 const char *name;
563
564 if (len < 2 || !buf)
565 return 0;
566
567 buf[0] = '\0';
568
569 for (i = 0; flags != 0; i++) {
570 bit = bd[i].bit;
571 name = bd[i].name;
572 if (bit == 0 && flags != 0) {
573 /* print any unnamed bits */
574 snprintf(hexstr, 16, "0x%X", flags);
575 name = hexstr;
576 flags = 0; /* exit loop */
577 } else if ((flags & bit) == 0)
578 continue;
579 flags &= ~bit;
580 nlen = strlen(name);
581 slen += nlen;
582 /* count btwn flag space */
583 if (flags != 0)
584 slen += 1;
585 /* need NULL char as well */
586 if (len <= slen)
587 break;
588 /* copy NULL char but don't count it */
589 strncpy(p, name, nlen + 1);
590 p += nlen;
591 /* copy btwn flag space and NULL char */
592 if (flags != 0)
593 p += snprintf(p, 2, " ");
594 len -= slen;
595 }
596
597 /* indicate the str was too short */
598 if (flags != 0) {
599 if (len < 2)
600 p -= 2 - len; /* overwrite last char */
601 p += snprintf(p, 2, ">");
602 }
603
604 return (int)(p - buf);
605}
606EXPORT_SYMBOL(brcmu_format_flags);
607
608/* print bytes formatted as hex to a string. return the resulting string length */
609int brcmu_format_hex(char *str, const void *bytes, int len)
610{
611 int i;
612 char *p = str;
613 const u8 *src = (const u8 *)bytes;
614
615 for (i = 0; i < len; i++) {
616 p += snprintf(p, 3, "%02X", *src);
617 src++;
618 }
619 return (int)(p - str);
620}
621EXPORT_SYMBOL(brcmu_format_hex);
622#endif /* defined(BCMDBG) */
623
624char *brcmu_chipname(uint chipid, char *buf, uint len)
625{
626 const char *fmt;
627
628 fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
629 snprintf(buf, len, fmt, chipid);
630 return buf;
631}
632EXPORT_SYMBOL(brcmu_chipname);
633
634uint brcmu_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)
635{
636 uint len;
637
638 len = strlen(name) + 1;
639
640 if ((len + datalen) > buflen)
641 return 0;
642
643 strncpy(buf, name, buflen);
644
645 /* append data onto the end of the name string */
646 memcpy(&buf[len], data, datalen);
647 len += datalen;
648
649 return len;
650}
651EXPORT_SYMBOL(brcmu_mkiovar);
652
653/* Quarter dBm units to mW
654 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
655 * Table is offset so the last entry is largest mW value that fits in
656 * a u16.
657 */
658
659#define QDBM_OFFSET 153 /* Offset for first entry */
660#define QDBM_TABLE_LEN 40 /* Table size */
661
662/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
663 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
664 */
665#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
666
667/* Largest mW value that will round down to the last table entry,
668 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
669 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
670 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
671 */
672#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
673
674static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
675/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
676/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
677/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
678/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
679/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
680/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
681};
682
683u16 brcmu_qdbm_to_mw(u8 qdbm)
684{
685 uint factor = 1;
686 int idx = qdbm - QDBM_OFFSET;
687
688 if (idx >= QDBM_TABLE_LEN) {
689 /* clamp to max u16 mW value */
690 return 0xFFFF;
691 }
692
693 /* scale the qdBm index up to the range of the table 0-40
694 * where an offset of 40 qdBm equals a factor of 10 mW.
695 */
696 while (idx < 0) {
697 idx += 40;
698 factor *= 10;
699 }
700
701 /* return the mW value scaled down to the correct factor of 10,
702 * adding in factor/2 to get proper rounding.
703 */
704 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
705}
706EXPORT_SYMBOL(brcmu_qdbm_to_mw);
707
708u8 brcmu_mw_to_qdbm(u16 mw)
709{
710 u8 qdbm;
711 int offset;
712 uint mw_uint = mw;
713 uint boundary;
714
715 /* handle boundary case */
716 if (mw_uint <= 1)
717 return 0;
718
719 offset = QDBM_OFFSET;
720
721 /* move mw into the range of the table */
722 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
723 mw_uint *= 10;
724 offset -= 40;
725 }
726
727 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
728 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
729 nqdBm_to_mW_map[qdbm]) / 2;
730 if (mw_uint < boundary)
731 break;
732 }
733
734 qdbm += (u8) offset;
735
736 return qdbm;
737}
738EXPORT_SYMBOL(brcmu_mw_to_qdbm);
739
740uint brcmu_bitcount(u8 *bitmap, uint length)
741{
742 uint bitcount = 0, i;
743 u8 tmp;
744 for (i = 0; i < length; i++) {
745 tmp = bitmap[i];
746 while (tmp) {
747 bitcount++;
748 tmp &= (tmp - 1);
749 }
750 }
751 return bitcount;
752}
753EXPORT_SYMBOL(brcmu_bitcount);
754
755/* Initialization of brcmu_strbuf structure */
756void brcmu_binit(struct brcmu_strbuf *b, char *buf, uint size)
757{
758 b->origsize = b->size = size;
759 b->origbuf = b->buf = buf;
760}
761EXPORT_SYMBOL(brcmu_binit);
762
763/* Buffer sprintf wrapper to guard against buffer overflow */
764int brcmu_bprintf(struct brcmu_strbuf *b, const char *fmt, ...)
765{
766 va_list ap;
767 int r;
768
769 va_start(ap, fmt);
770 r = vsnprintf(b->buf, b->size, fmt, ap);
771
772 /* Non Ansi C99 compliant returns -1,
773 * Ansi compliant return r >= b->size,
774 * stdlib returns 0, handle all
775 */
776 if ((r == -1) || (r >= (int)b->size) || (r == 0)) {
777 b->size = 0;
778 } else {
779 b->size -= r;
780 b->buf += r;
781 }
782
783 va_end(ap);
784
785 return r;
786}
787EXPORT_SYMBOL(brcmu_bprintf);
diff --git a/drivers/staging/brcm80211/brcmutil/wifi.c b/drivers/staging/brcm80211/brcmutil/wifi.c
new file mode 100644
index 00000000000..b9ffe8682a2
--- /dev/null
+++ b/drivers/staging/brcm80211/brcmutil/wifi.c
@@ -0,0 +1,131 @@
1/*
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#include <brcmu_wifi.h>
17
18/*
19 * Verify the chanspec is using a legal set of parameters, i.e. that the
20 * chanspec specified a band, bw, ctl_sb and channel and that the
21 * combination could be legal given any set of circumstances.
22 * RETURNS: true is the chanspec is malformed, false if it looks good.
23 */
24bool brcmu_chspec_malformed(chanspec_t chanspec)
25{
26 /* must be 2G or 5G band */
27 if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec))
28 return true;
29 /* must be 20 or 40 bandwidth */
30 if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec))
31 return true;
32
33 /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */
34 if (CHSPEC_IS20(chanspec)) {
35 if (!CHSPEC_SB_NONE(chanspec))
36 return true;
37 } else {
38 if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec))
39 return true;
40 }
41
42 return false;
43}
44EXPORT_SYMBOL(brcmu_chspec_malformed);
45
46/*
47 * This function returns the channel number that control traffic is being sent on, for legacy
48 * channels this is just the channel number, for 40MHZ channels it is the upper or lowre 20MHZ
49 * sideband depending on the chanspec selected
50 */
51u8 brcmu_chspec_ctlchan(chanspec_t chspec)
52{
53 u8 ctl_chan;
54
55 /* Is there a sideband ? */
56 if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) {
57 return CHSPEC_CHANNEL(chspec);
58 } else {
59 /* we only support 40MHZ with sidebands */
60 /* chanspec channel holds the centre frequency, use that and the
61 * side band information to reconstruct the control channel number
62 */
63 if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) {
64 /* control chan is the upper 20 MHZ SB of the 40MHZ channel */
65 ctl_chan = UPPER_20_SB(CHSPEC_CHANNEL(chspec));
66 } else {
67 /* control chan is the lower 20 MHZ SB of the 40MHZ channel */
68 ctl_chan = LOWER_20_SB(CHSPEC_CHANNEL(chspec));
69 }
70 }
71
72 return ctl_chan;
73}
74EXPORT_SYMBOL(brcmu_chspec_ctlchan);
75
76/*
77 * Return the channel number for a given frequency and base frequency.
78 * The returned channel number is relative to the given base frequency.
79 * If the given base frequency is zero, a base frequency of 5 GHz is assumed for
80 * frequencies from 5 - 6 GHz, and 2.407 GHz is assumed for 2.4 - 2.5 GHz.
81 *
82 * Frequency is specified in MHz.
83 * The base frequency is specified as (start_factor * 500 kHz).
84 * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for
85 * 2.4 GHz and 5 GHz bands.
86 *
87 * The returned channel will be in the range [1, 14] in the 2.4 GHz band
88 * and [0, 200] otherwise.
89 * -1 is returned if the start_factor is WF_CHAN_FACTOR_2_4_G and the
90 * frequency is not a 2.4 GHz channel, or if the frequency is not and even
91 * multiple of 5 MHz from the base frequency to the base plus 1 GHz.
92 *
93 * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2
94 */
95int brcmu_mhz2channel(uint freq, uint start_factor)
96{
97 int ch = -1;
98 uint base;
99 int offset;
100
101 /* take the default channel start frequency */
102 if (start_factor == 0) {
103 if (freq >= 2400 && freq <= 2500)
104 start_factor = WF_CHAN_FACTOR_2_4_G;
105 else if (freq >= 5000 && freq <= 6000)
106 start_factor = WF_CHAN_FACTOR_5_G;
107 }
108
109 if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G)
110 return 14;
111
112 base = start_factor / 2;
113
114 /* check that the frequency is in 1GHz range of the base */
115 if ((freq < base) || (freq > base + 1000))
116 return -1;
117
118 offset = freq - base;
119 ch = offset / 5;
120
121 /* check that frequency is a 5MHz multiple from the base */
122 if (offset != (ch * 5))
123 return -1;
124
125 /* restricted channel range check for 2.4G */
126 if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13))
127 return -1;
128
129 return ch;
130}
131EXPORT_SYMBOL(brcmu_mhz2channel);