2 * Copyright (c) 2010 Broadcom Corporation
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.
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.
17 #include <linux/netdevice.h>
18 #include <brcmu_utils.h>
20 MODULE_AUTHOR("Broadcom Corporation");
21 MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities.");
22 MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
23 MODULE_LICENSE("Dual BSD/GPL");
25 struct sk_buff
*brcmu_pkt_buf_get_skb(uint len
)
29 skb
= dev_alloc_skb(len
);
37 EXPORT_SYMBOL(brcmu_pkt_buf_get_skb
);
39 /* Free the driver packet. Free the tag if present */
40 void brcmu_pkt_buf_free_skb(struct sk_buff
*skb
)
45 /* perversion: we use skb->next to chain multi-skb packets */
51 /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if
54 dev_kfree_skb_any(skb
);
56 /* can free immediately (even in_irq()) if destructor
65 EXPORT_SYMBOL(brcmu_pkt_buf_free_skb
);
68 /* copy a buffer into a pkt buffer chain */
69 uint
brcmu_pktfrombuf(struct sk_buff
*p
, uint offset
, int len
,
74 /* skip 'offset' bytes */
75 for (; p
&& offset
; p
= p
->next
) {
76 if (offset
< (uint
) (p
->len
))
85 for (; p
&& len
; p
= p
->next
) {
86 n
= min((uint
) (p
->len
) - offset
, (uint
) len
);
87 memcpy(p
->data
+ offset
, buf
, n
);
96 EXPORT_SYMBOL(brcmu_pktfrombuf
);
98 /* return total length of buffer chain */
99 uint
brcmu_pkttotlen(struct sk_buff
*p
)
104 for (; p
; p
= p
->next
)
108 EXPORT_SYMBOL(brcmu_pkttotlen
);
111 * osl multiple-precedence packet queue
112 * hi_prec is always >= the number of the highest non-empty precedence
114 struct sk_buff
*brcmu_pktq_penq(struct pktq
*pq
, int prec
,
119 if (pktq_full(pq
) || pktq_pfull(pq
, prec
))
134 if (pq
->hi_prec
< prec
)
135 pq
->hi_prec
= (u8
) prec
;
139 EXPORT_SYMBOL(brcmu_pktq_penq
);
141 struct sk_buff
*brcmu_pktq_penq_head(struct pktq
*pq
, int prec
,
146 if (pktq_full(pq
) || pktq_pfull(pq
, prec
))
160 if (pq
->hi_prec
< prec
)
161 pq
->hi_prec
= (u8
) prec
;
165 EXPORT_SYMBOL(brcmu_pktq_penq_head
);
167 struct sk_buff
*brcmu_pktq_pdeq(struct pktq
*pq
, int prec
)
190 EXPORT_SYMBOL(brcmu_pktq_pdeq
);
192 struct sk_buff
*brcmu_pktq_pdeq_tail(struct pktq
*pq
, int prec
)
195 struct sk_buff
*p
, *prev
;
203 for (prev
= NULL
; p
!= q
->tail
; p
= p
->prev
)
218 EXPORT_SYMBOL(brcmu_pktq_pdeq_tail
);
221 brcmu_pktq_pflush(struct pktq
*pq
, int prec
, bool dir
,
222 ifpkt_cb_t fn
, void *arg
)
225 struct sk_buff
*p
, *prev
= NULL
;
230 if (fn
== NULL
|| (*fn
) (p
, arg
)) {
231 bool head
= (p
== q
->head
);
235 prev
->prev
= p
->prev
;
237 brcmu_pkt_buf_free_skb(p
);
240 p
= (head
? q
->head
: prev
->prev
);
247 if (q
->head
== NULL
) {
251 EXPORT_SYMBOL(brcmu_pktq_pflush
);
253 void brcmu_pktq_flush(struct pktq
*pq
, bool dir
,
254 ifpkt_cb_t fn
, void *arg
)
257 for (prec
= 0; prec
< pq
->num_prec
; prec
++)
258 brcmu_pktq_pflush(pq
, prec
, dir
, fn
, arg
);
260 EXPORT_SYMBOL(brcmu_pktq_flush
);
262 void brcmu_pktq_init(struct pktq
*pq
, int num_prec
, int max_len
)
266 /* pq is variable size; only zero out what's requested */
268 offsetof(struct pktq
, q
) + (sizeof(struct pktq_prec
) * num_prec
));
270 pq
->num_prec
= (u16
) num_prec
;
272 pq
->max
= (u16
) max_len
;
274 for (prec
= 0; prec
< num_prec
; prec
++)
275 pq
->q
[prec
].max
= pq
->max
;
277 EXPORT_SYMBOL(brcmu_pktq_init
);
279 struct sk_buff
*brcmu_pktq_peek_tail(struct pktq
*pq
, int *prec_out
)
286 for (prec
= 0; prec
< pq
->hi_prec
; prec
++)
287 if (pq
->q
[prec
].head
)
293 return pq
->q
[prec
].tail
;
295 EXPORT_SYMBOL(brcmu_pktq_peek_tail
);
297 /* Return sum of lengths of a specific set of precedences */
298 int brcmu_pktq_mlen(struct pktq
*pq
, uint prec_bmp
)
304 for (prec
= 0; prec
<= pq
->hi_prec
; prec
++)
305 if (prec_bmp
& (1 << prec
))
306 len
+= pq
->q
[prec
].len
;
310 EXPORT_SYMBOL(brcmu_pktq_mlen
);
312 /* Priority dequeue from a specific set of precedences */
313 struct sk_buff
*brcmu_pktq_mdeq(struct pktq
*pq
, uint prec_bmp
,
323 while ((prec
= pq
->hi_prec
) > 0 && pq
->q
[prec
].head
== NULL
)
326 while ((prec_bmp
& (1 << prec
)) == 0 || pq
->q
[prec
].head
== NULL
)
351 EXPORT_SYMBOL(brcmu_pktq_mdeq
);
353 /* parse a xx:xx:xx:xx:xx:xx format ethernet address */
354 int brcmu_ether_atoe(char *p
, u8
*ea
)
359 ea
[i
++] = (char)simple_strtoul(p
, &p
, 16);
366 EXPORT_SYMBOL(brcmu_ether_atoe
);
369 /* pretty hex print a pkt buffer chain */
370 void brcmu_prpkt(const char *msg
, struct sk_buff
*p0
)
374 if (msg
&& (msg
[0] != '\0'))
375 printk(KERN_DEBUG
"%s:\n", msg
);
377 for (p
= p0
; p
; p
= p
->next
)
378 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET
, p
->data
, p
->len
);
380 EXPORT_SYMBOL(brcmu_prpkt
);
381 #endif /* defined(BCMDBG) */
383 /* iovar table lookup */
384 const struct brcmu_iovar
*brcmu_iovar_lookup(const struct brcmu_iovar
*table
,
387 const struct brcmu_iovar
*vi
;
388 const char *lookup_name
;
390 /* skip any ':' delimited option prefixes */
391 lookup_name
= strrchr(name
, ':');
392 if (lookup_name
!= NULL
)
397 for (vi
= table
; vi
->name
; vi
++) {
398 if (!strcmp(vi
->name
, lookup_name
))
401 /* ran to end of table */
403 return NULL
; /* var name not found */
405 EXPORT_SYMBOL(brcmu_iovar_lookup
);
407 int brcmu_iovar_lencheck(const struct brcmu_iovar
*vi
, void *arg
, int len
,
412 /* length check on io buf */
421 /* all integers are s32 sized args at the ioctl interface */
422 if (len
< (int)sizeof(int)) {
423 bcmerror
= -EOVERFLOW
;
428 /* buffer must meet minimum length requirement */
429 if (len
< vi
->minlen
) {
430 bcmerror
= -EOVERFLOW
;
436 /* Cannot return nil... */
437 bcmerror
= -ENOTSUPP
;
439 /* Set is an action w/o parameters */
445 /* unknown type for length check in iovar info */
446 bcmerror
= -ENOTSUPP
;
451 EXPORT_SYMBOL(brcmu_iovar_lencheck
);
453 /*******************************************************************************
456 * Computes a crc8 over the input data using the polynomial:
458 * x^8 + x^7 +x^6 + x^4 + x^2 + 1
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.
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
472 * ****************************************************************************
475 static 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
510 u8
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 */
514 /* loop over the buffer data */
516 crc
= crc8_table
[(crc
^ *pdata
++) & 0xff];
520 EXPORT_SYMBOL(brcmu_crc8
);
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
527 struct brcmu_tlv
*brcmu_parse_tlvs(void *buf
, int buflen
, uint key
)
529 struct brcmu_tlv
*elt
;
532 elt
= (struct brcmu_tlv
*) buf
;
535 /* find tagged parameter */
536 while (totlen
>= 2) {
539 /* validate remaining totlen */
540 if ((elt
->id
== key
) && (totlen
>= (len
+ 2)))
543 elt
= (struct brcmu_tlv
*) ((u8
*) elt
+ (len
+ 2));
549 EXPORT_SYMBOL(brcmu_parse_tlvs
);
554 brcmu_format_flags(const struct brcmu_bit_desc
*bd
, u32 flags
, char *buf
,
560 int slen
= 0, nlen
= 0;
569 for (i
= 0; flags
!= 0; i
++) {
572 if (bit
== 0 && flags
!= 0) {
573 /* print any unnamed bits */
574 snprintf(hexstr
, 16, "0x%X", flags
);
576 flags
= 0; /* exit loop */
577 } else if ((flags
& bit
) == 0)
582 /* count btwn flag space */
585 /* need NULL char as well */
588 /* copy NULL char but don't count it */
589 strncpy(p
, name
, nlen
+ 1);
591 /* copy btwn flag space and NULL char */
593 p
+= snprintf(p
, 2, " ");
597 /* indicate the str was too short */
600 p
-= 2 - len
; /* overwrite last char */
601 p
+= snprintf(p
, 2, ">");
604 return (int)(p
- buf
);
606 EXPORT_SYMBOL(brcmu_format_flags
);
608 /* print bytes formatted as hex to a string. return the resulting string length */
609 int brcmu_format_hex(char *str
, const void *bytes
, int len
)
613 const u8
*src
= (const u8
*)bytes
;
615 for (i
= 0; i
< len
; i
++) {
616 p
+= snprintf(p
, 3, "%02X", *src
);
619 return (int)(p
- str
);
621 EXPORT_SYMBOL(brcmu_format_hex
);
622 #endif /* defined(BCMDBG) */
624 char *brcmu_chipname(uint chipid
, char *buf
, uint len
)
628 fmt
= ((chipid
> 0xa000) || (chipid
< 0x4000)) ? "%d" : "%x";
629 snprintf(buf
, len
, fmt
, chipid
);
632 EXPORT_SYMBOL(brcmu_chipname
);
634 uint
brcmu_mkiovar(char *name
, char *data
, uint datalen
, char *buf
, uint buflen
)
638 len
= strlen(name
) + 1;
640 if ((len
+ datalen
) > buflen
)
643 strncpy(buf
, name
, buflen
);
645 /* append data onto the end of the name string */
646 memcpy(&buf
[len
], data
, datalen
);
651 EXPORT_SYMBOL(brcmu_mkiovar
);
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
659 #define QDBM_OFFSET 153 /* Offset for first entry */
660 #define QDBM_TABLE_LEN 40 /* Table size */
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
665 #define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
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.
672 #define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
674 static 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
683 u16
brcmu_qdbm_to_mw(u8 qdbm
)
686 int idx
= qdbm
- QDBM_OFFSET
;
688 if (idx
>= QDBM_TABLE_LEN
) {
689 /* clamp to max u16 mW value */
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.
701 /* return the mW value scaled down to the correct factor of 10,
702 * adding in factor/2 to get proper rounding.
704 return (nqdBm_to_mW_map
[idx
] + factor
/ 2) / factor
;
706 EXPORT_SYMBOL(brcmu_qdbm_to_mw
);
708 u8
brcmu_mw_to_qdbm(u16 mw
)
715 /* handle boundary case */
719 offset
= QDBM_OFFSET
;
721 /* move mw into the range of the table */
722 while (mw_uint
< QDBM_TABLE_LOW_BOUND
) {
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
)
738 EXPORT_SYMBOL(brcmu_mw_to_qdbm
);
740 uint
brcmu_bitcount(u8
*bitmap
, uint length
)
742 uint bitcount
= 0, i
;
744 for (i
= 0; i
< length
; i
++) {
753 EXPORT_SYMBOL(brcmu_bitcount
);
755 /* Initialization of brcmu_strbuf structure */
756 void brcmu_binit(struct brcmu_strbuf
*b
, char *buf
, uint size
)
758 b
->origsize
= b
->size
= size
;
759 b
->origbuf
= b
->buf
= buf
;
761 EXPORT_SYMBOL(brcmu_binit
);
763 /* Buffer sprintf wrapper to guard against buffer overflow */
764 int brcmu_bprintf(struct brcmu_strbuf
*b
, const char *fmt
, ...)
770 r
= vsnprintf(b
->buf
, b
->size
, fmt
, ap
);
772 /* Non Ansi C99 compliant returns -1,
773 * Ansi compliant return r >= b->size,
774 * stdlib returns 0, handle all
776 if ((r
== -1) || (r
>= (int)b
->size
) || (r
== 0)) {
787 EXPORT_SYMBOL(brcmu_bprintf
);