162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2010 Broadcom Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/netdevice.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <brcmu_utils.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom Corporation"); 1462306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities."); 1562306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct sk_buff *brcmu_pkt_buf_get_skb(uint len) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci struct sk_buff *skb; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci skb = dev_alloc_skb(len); 2262306a36Sopenharmony_ci if (skb) { 2362306a36Sopenharmony_ci skb_put(skb, len); 2462306a36Sopenharmony_ci skb->priority = 0; 2562306a36Sopenharmony_ci } 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci return skb; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pkt_buf_get_skb); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Free the driver packet. Free the tag if present */ 3262306a36Sopenharmony_civoid brcmu_pkt_buf_free_skb(struct sk_buff *skb) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci if (!skb) 3562306a36Sopenharmony_ci return; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci WARN_ON(skb->next); 3862306a36Sopenharmony_ci dev_kfree_skb_any(skb); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pkt_buf_free_skb); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * osl multiple-precedence packet queue 4462306a36Sopenharmony_ci * hi_prec is always >= the number of the highest non-empty precedence 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistruct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, 4762306a36Sopenharmony_ci struct sk_buff *p) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct sk_buff_head *q; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (pktq_full(pq) || pktq_pfull(pq, prec)) 5262306a36Sopenharmony_ci return NULL; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci q = &pq->q[prec].skblist; 5562306a36Sopenharmony_ci skb_queue_tail(q, p); 5662306a36Sopenharmony_ci pq->len++; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (pq->hi_prec < prec) 5962306a36Sopenharmony_ci pq->hi_prec = (u8) prec; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return p; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_penq); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, 6662306a36Sopenharmony_ci struct sk_buff *p) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct sk_buff_head *q; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (pktq_full(pq) || pktq_pfull(pq, prec)) 7162306a36Sopenharmony_ci return NULL; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci q = &pq->q[prec].skblist; 7462306a36Sopenharmony_ci skb_queue_head(q, p); 7562306a36Sopenharmony_ci pq->len++; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (pq->hi_prec < prec) 7862306a36Sopenharmony_ci pq->hi_prec = (u8) prec; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return p; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_penq_head); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct sk_buff_head *q; 8762306a36Sopenharmony_ci struct sk_buff *p; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci q = &pq->q[prec].skblist; 9062306a36Sopenharmony_ci p = skb_dequeue(q); 9162306a36Sopenharmony_ci if (p == NULL) 9262306a36Sopenharmony_ci return NULL; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci pq->len--; 9562306a36Sopenharmony_ci return p; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_pdeq); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * precedence based dequeue with match function. Passing a NULL pointer 10162306a36Sopenharmony_ci * for the match function parameter is considered to be a wildcard so 10262306a36Sopenharmony_ci * any packet on the queue is returned. In that case it is no different 10362306a36Sopenharmony_ci * from brcmu_pktq_pdeq() above. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistruct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec, 10662306a36Sopenharmony_ci bool (*match_fn)(struct sk_buff *skb, 10762306a36Sopenharmony_ci void *arg), void *arg) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct sk_buff_head *q; 11062306a36Sopenharmony_ci struct sk_buff *p, *next; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci q = &pq->q[prec].skblist; 11362306a36Sopenharmony_ci skb_queue_walk_safe(q, p, next) { 11462306a36Sopenharmony_ci if (match_fn == NULL || match_fn(p, arg)) { 11562306a36Sopenharmony_ci skb_unlink(p, q); 11662306a36Sopenharmony_ci pq->len--; 11762306a36Sopenharmony_ci return p; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci return NULL; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_pdeq_match); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct sk_buff_head *q; 12762306a36Sopenharmony_ci struct sk_buff *p; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci q = &pq->q[prec].skblist; 13062306a36Sopenharmony_ci p = skb_dequeue_tail(q); 13162306a36Sopenharmony_ci if (p == NULL) 13262306a36Sopenharmony_ci return NULL; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci pq->len--; 13562306a36Sopenharmony_ci return p; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_pdeq_tail); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_civoid 14062306a36Sopenharmony_cibrcmu_pktq_pflush(struct pktq *pq, int prec, bool dir, 14162306a36Sopenharmony_ci bool (*fn)(struct sk_buff *, void *), void *arg) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct sk_buff_head *q; 14462306a36Sopenharmony_ci struct sk_buff *p, *next; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci q = &pq->q[prec].skblist; 14762306a36Sopenharmony_ci skb_queue_walk_safe(q, p, next) { 14862306a36Sopenharmony_ci if (fn == NULL || (*fn) (p, arg)) { 14962306a36Sopenharmony_ci skb_unlink(p, q); 15062306a36Sopenharmony_ci brcmu_pkt_buf_free_skb(p); 15162306a36Sopenharmony_ci pq->len--; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_pflush); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_civoid brcmu_pktq_flush(struct pktq *pq, bool dir, 15862306a36Sopenharmony_ci bool (*fn)(struct sk_buff *, void *), void *arg) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci int prec; 16162306a36Sopenharmony_ci for (prec = 0; prec < pq->num_prec; prec++) 16262306a36Sopenharmony_ci brcmu_pktq_pflush(pq, prec, dir, fn, arg); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_flush); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_civoid brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci int prec; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* pq is variable size; only zero out what's requested */ 17162306a36Sopenharmony_ci memset(pq, 0, 17262306a36Sopenharmony_ci offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec)); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci pq->num_prec = (u16) num_prec; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci pq->max = (u16) max_len; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci for (prec = 0; prec < num_prec; prec++) { 17962306a36Sopenharmony_ci pq->q[prec].max = pq->max; 18062306a36Sopenharmony_ci skb_queue_head_init(&pq->q[prec].skblist); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_init); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistruct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci int prec; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (pktq_empty(pq)) 19062306a36Sopenharmony_ci return NULL; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci for (prec = 0; prec < pq->hi_prec; prec++) 19362306a36Sopenharmony_ci if (!skb_queue_empty(&pq->q[prec].skblist)) 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (prec_out) 19762306a36Sopenharmony_ci *prec_out = prec; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return skb_peek_tail(&pq->q[prec].skblist); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_peek_tail); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci/* Return sum of lengths of a specific set of precedences */ 20462306a36Sopenharmony_ciint brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int prec, len; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci len = 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci for (prec = 0; prec <= pq->hi_prec; prec++) 21162306a36Sopenharmony_ci if (prec_bmp & (1 << prec)) 21262306a36Sopenharmony_ci len += pq->q[prec].skblist.qlen; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return len; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_mlen); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* Priority dequeue from a specific set of precedences */ 21962306a36Sopenharmony_cistruct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, 22062306a36Sopenharmony_ci int *prec_out) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct sk_buff_head *q; 22362306a36Sopenharmony_ci struct sk_buff *p; 22462306a36Sopenharmony_ci int prec; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (pktq_empty(pq)) 22762306a36Sopenharmony_ci return NULL; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci while ((prec = pq->hi_prec) > 0 && 23062306a36Sopenharmony_ci skb_queue_empty(&pq->q[prec].skblist)) 23162306a36Sopenharmony_ci pq->hi_prec--; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci while ((prec_bmp & (1 << prec)) == 0 || 23462306a36Sopenharmony_ci skb_queue_empty(&pq->q[prec].skblist)) 23562306a36Sopenharmony_ci if (prec-- == 0) 23662306a36Sopenharmony_ci return NULL; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci q = &pq->q[prec].skblist; 23962306a36Sopenharmony_ci p = skb_dequeue(q); 24062306a36Sopenharmony_ci if (p == NULL) 24162306a36Sopenharmony_ci return NULL; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci pq->len--; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (prec_out) 24662306a36Sopenharmony_ci *prec_out = prec; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return p; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_mdeq); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* Produce a human-readable string for boardrev */ 25362306a36Sopenharmony_cichar *brcmu_boardrev_str(u32 brev, char *buf) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci char c; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (brev < 0x100) { 25862306a36Sopenharmony_ci snprintf(buf, BRCMU_BOARDREV_LEN, "%d.%d", 25962306a36Sopenharmony_ci (brev & 0xf0) >> 4, brev & 0xf); 26062306a36Sopenharmony_ci } else { 26162306a36Sopenharmony_ci c = (brev & 0xf000) == 0x1000 ? 'P' : 'A'; 26262306a36Sopenharmony_ci snprintf(buf, BRCMU_BOARDREV_LEN, "%c%03x", c, brev & 0xfff); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci return buf; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_boardrev_str); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cichar *brcmu_dotrev_str(u32 dotrev, char *buf) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci u8 dotval[4]; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (!dotrev) { 27362306a36Sopenharmony_ci snprintf(buf, BRCMU_DOTREV_LEN, "unknown"); 27462306a36Sopenharmony_ci return buf; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci dotval[0] = (dotrev >> 24) & 0xFF; 27762306a36Sopenharmony_ci dotval[1] = (dotrev >> 16) & 0xFF; 27862306a36Sopenharmony_ci dotval[2] = (dotrev >> 8) & 0xFF; 27962306a36Sopenharmony_ci dotval[3] = dotrev & 0xFF; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (dotval[3]) 28262306a36Sopenharmony_ci snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d.%d", dotval[0], 28362306a36Sopenharmony_ci dotval[1], dotval[2], dotval[3]); 28462306a36Sopenharmony_ci else if (dotval[2]) 28562306a36Sopenharmony_ci snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d", dotval[0], 28662306a36Sopenharmony_ci dotval[1], dotval[2]); 28762306a36Sopenharmony_ci else 28862306a36Sopenharmony_ci snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d", dotval[0], 28962306a36Sopenharmony_ci dotval[1]); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return buf; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_dotrev_str); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci#if defined(DEBUG) 29662306a36Sopenharmony_ci/* pretty hex print a pkt buffer chain */ 29762306a36Sopenharmony_civoid brcmu_prpkt(const char *msg, struct sk_buff *p0) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct sk_buff *p; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (msg && (msg[0] != '\0')) 30262306a36Sopenharmony_ci pr_debug("%s:\n", msg); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci for (p = p0; p; p = p->next) 30562306a36Sopenharmony_ci print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_prpkt); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_civoid brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct va_format vaf; 31262306a36Sopenharmony_ci va_list args; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci va_start(args, fmt); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci vaf.fmt = fmt; 31762306a36Sopenharmony_ci vaf.va = &args; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci pr_debug("%pV", &vaf); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci va_end(args); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ciEXPORT_SYMBOL(brcmu_dbg_hex_dump); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci#endif /* defined(DEBUG) */ 328