18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2010 Broadcom Corporation 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <brcmu_utils.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Broadcom Corporation"); 148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities."); 158c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards"); 168c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct sk_buff *brcmu_pkt_buf_get_skb(uint len) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct sk_buff *skb; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci skb = dev_alloc_skb(len); 238c2ecf20Sopenharmony_ci if (skb) { 248c2ecf20Sopenharmony_ci skb_put(skb, len); 258c2ecf20Sopenharmony_ci skb->priority = 0; 268c2ecf20Sopenharmony_ci } 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci return skb; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pkt_buf_get_skb); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Free the driver packet. Free the tag if present */ 338c2ecf20Sopenharmony_civoid brcmu_pkt_buf_free_skb(struct sk_buff *skb) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci if (!skb) 368c2ecf20Sopenharmony_ci return; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci WARN_ON(skb->next); 398c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pkt_buf_free_skb); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * osl multiple-precedence packet queue 458c2ecf20Sopenharmony_ci * hi_prec is always >= the number of the highest non-empty precedence 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_cistruct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, 488c2ecf20Sopenharmony_ci struct sk_buff *p) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct sk_buff_head *q; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (pktq_full(pq) || pktq_pfull(pq, prec)) 538c2ecf20Sopenharmony_ci return NULL; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci q = &pq->q[prec].skblist; 568c2ecf20Sopenharmony_ci skb_queue_tail(q, p); 578c2ecf20Sopenharmony_ci pq->len++; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (pq->hi_prec < prec) 608c2ecf20Sopenharmony_ci pq->hi_prec = (u8) prec; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return p; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_penq); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, 678c2ecf20Sopenharmony_ci struct sk_buff *p) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct sk_buff_head *q; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (pktq_full(pq) || pktq_pfull(pq, prec)) 728c2ecf20Sopenharmony_ci return NULL; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci q = &pq->q[prec].skblist; 758c2ecf20Sopenharmony_ci skb_queue_head(q, p); 768c2ecf20Sopenharmony_ci pq->len++; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (pq->hi_prec < prec) 798c2ecf20Sopenharmony_ci pq->hi_prec = (u8) prec; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return p; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_penq_head); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct sk_buff_head *q; 888c2ecf20Sopenharmony_ci struct sk_buff *p; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci q = &pq->q[prec].skblist; 918c2ecf20Sopenharmony_ci p = skb_dequeue(q); 928c2ecf20Sopenharmony_ci if (p == NULL) 938c2ecf20Sopenharmony_ci return NULL; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci pq->len--; 968c2ecf20Sopenharmony_ci return p; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_pdeq); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * precedence based dequeue with match function. Passing a NULL pointer 1028c2ecf20Sopenharmony_ci * for the match function parameter is considered to be a wildcard so 1038c2ecf20Sopenharmony_ci * any packet on the queue is returned. In that case it is no different 1048c2ecf20Sopenharmony_ci * from brcmu_pktq_pdeq() above. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_cistruct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec, 1078c2ecf20Sopenharmony_ci bool (*match_fn)(struct sk_buff *skb, 1088c2ecf20Sopenharmony_ci void *arg), void *arg) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct sk_buff_head *q; 1118c2ecf20Sopenharmony_ci struct sk_buff *p, *next; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci q = &pq->q[prec].skblist; 1148c2ecf20Sopenharmony_ci skb_queue_walk_safe(q, p, next) { 1158c2ecf20Sopenharmony_ci if (match_fn == NULL || match_fn(p, arg)) { 1168c2ecf20Sopenharmony_ci skb_unlink(p, q); 1178c2ecf20Sopenharmony_ci pq->len--; 1188c2ecf20Sopenharmony_ci return p; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci return NULL; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_pdeq_match); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistruct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct sk_buff_head *q; 1288c2ecf20Sopenharmony_ci struct sk_buff *p; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci q = &pq->q[prec].skblist; 1318c2ecf20Sopenharmony_ci p = skb_dequeue_tail(q); 1328c2ecf20Sopenharmony_ci if (p == NULL) 1338c2ecf20Sopenharmony_ci return NULL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci pq->len--; 1368c2ecf20Sopenharmony_ci return p; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_pdeq_tail); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_civoid 1418c2ecf20Sopenharmony_cibrcmu_pktq_pflush(struct pktq *pq, int prec, bool dir, 1428c2ecf20Sopenharmony_ci bool (*fn)(struct sk_buff *, void *), void *arg) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct sk_buff_head *q; 1458c2ecf20Sopenharmony_ci struct sk_buff *p, *next; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci q = &pq->q[prec].skblist; 1488c2ecf20Sopenharmony_ci skb_queue_walk_safe(q, p, next) { 1498c2ecf20Sopenharmony_ci if (fn == NULL || (*fn) (p, arg)) { 1508c2ecf20Sopenharmony_ci skb_unlink(p, q); 1518c2ecf20Sopenharmony_ci brcmu_pkt_buf_free_skb(p); 1528c2ecf20Sopenharmony_ci pq->len--; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_pflush); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_civoid brcmu_pktq_flush(struct pktq *pq, bool dir, 1598c2ecf20Sopenharmony_ci bool (*fn)(struct sk_buff *, void *), void *arg) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int prec; 1628c2ecf20Sopenharmony_ci for (prec = 0; prec < pq->num_prec; prec++) 1638c2ecf20Sopenharmony_ci brcmu_pktq_pflush(pq, prec, dir, fn, arg); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_flush); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_civoid brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int prec; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* pq is variable size; only zero out what's requested */ 1728c2ecf20Sopenharmony_ci memset(pq, 0, 1738c2ecf20Sopenharmony_ci offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec)); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci pq->num_prec = (u16) num_prec; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci pq->max = (u16) max_len; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci for (prec = 0; prec < num_prec; prec++) { 1808c2ecf20Sopenharmony_ci pq->q[prec].max = pq->max; 1818c2ecf20Sopenharmony_ci skb_queue_head_init(&pq->q[prec].skblist); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_init); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistruct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci int prec; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (pq->len == 0) 1918c2ecf20Sopenharmony_ci return NULL; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci for (prec = 0; prec < pq->hi_prec; prec++) 1948c2ecf20Sopenharmony_ci if (!skb_queue_empty(&pq->q[prec].skblist)) 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (prec_out) 1988c2ecf20Sopenharmony_ci *prec_out = prec; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return skb_peek_tail(&pq->q[prec].skblist); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_peek_tail); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* Return sum of lengths of a specific set of precedences */ 2058c2ecf20Sopenharmony_ciint brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci int prec, len; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci len = 0; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for (prec = 0; prec <= pq->hi_prec; prec++) 2128c2ecf20Sopenharmony_ci if (prec_bmp & (1 << prec)) 2138c2ecf20Sopenharmony_ci len += pq->q[prec].skblist.qlen; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return len; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_mlen); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* Priority dequeue from a specific set of precedences */ 2208c2ecf20Sopenharmony_cistruct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, 2218c2ecf20Sopenharmony_ci int *prec_out) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct sk_buff_head *q; 2248c2ecf20Sopenharmony_ci struct sk_buff *p; 2258c2ecf20Sopenharmony_ci int prec; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (pq->len == 0) 2288c2ecf20Sopenharmony_ci return NULL; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci while ((prec = pq->hi_prec) > 0 && 2318c2ecf20Sopenharmony_ci skb_queue_empty(&pq->q[prec].skblist)) 2328c2ecf20Sopenharmony_ci pq->hi_prec--; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci while ((prec_bmp & (1 << prec)) == 0 || 2358c2ecf20Sopenharmony_ci skb_queue_empty(&pq->q[prec].skblist)) 2368c2ecf20Sopenharmony_ci if (prec-- == 0) 2378c2ecf20Sopenharmony_ci return NULL; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci q = &pq->q[prec].skblist; 2408c2ecf20Sopenharmony_ci p = skb_dequeue(q); 2418c2ecf20Sopenharmony_ci if (p == NULL) 2428c2ecf20Sopenharmony_ci return NULL; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci pq->len--; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (prec_out) 2478c2ecf20Sopenharmony_ci *prec_out = prec; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return p; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_pktq_mdeq); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* Produce a human-readable string for boardrev */ 2548c2ecf20Sopenharmony_cichar *brcmu_boardrev_str(u32 brev, char *buf) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci char c; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (brev < 0x100) { 2598c2ecf20Sopenharmony_ci snprintf(buf, BRCMU_BOARDREV_LEN, "%d.%d", 2608c2ecf20Sopenharmony_ci (brev & 0xf0) >> 4, brev & 0xf); 2618c2ecf20Sopenharmony_ci } else { 2628c2ecf20Sopenharmony_ci c = (brev & 0xf000) == 0x1000 ? 'P' : 'A'; 2638c2ecf20Sopenharmony_ci snprintf(buf, BRCMU_BOARDREV_LEN, "%c%03x", c, brev & 0xfff); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci return buf; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_boardrev_str); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cichar *brcmu_dotrev_str(u32 dotrev, char *buf) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci u8 dotval[4]; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (!dotrev) { 2748c2ecf20Sopenharmony_ci snprintf(buf, BRCMU_DOTREV_LEN, "unknown"); 2758c2ecf20Sopenharmony_ci return buf; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci dotval[0] = (dotrev >> 24) & 0xFF; 2788c2ecf20Sopenharmony_ci dotval[1] = (dotrev >> 16) & 0xFF; 2798c2ecf20Sopenharmony_ci dotval[2] = (dotrev >> 8) & 0xFF; 2808c2ecf20Sopenharmony_ci dotval[3] = dotrev & 0xFF; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (dotval[3]) 2838c2ecf20Sopenharmony_ci snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d.%d", dotval[0], 2848c2ecf20Sopenharmony_ci dotval[1], dotval[2], dotval[3]); 2858c2ecf20Sopenharmony_ci else if (dotval[2]) 2868c2ecf20Sopenharmony_ci snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d", dotval[0], 2878c2ecf20Sopenharmony_ci dotval[1], dotval[2]); 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d", dotval[0], 2908c2ecf20Sopenharmony_ci dotval[1]); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return buf; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_dotrev_str); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci#if defined(DEBUG) 2978c2ecf20Sopenharmony_ci/* pretty hex print a pkt buffer chain */ 2988c2ecf20Sopenharmony_civoid brcmu_prpkt(const char *msg, struct sk_buff *p0) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct sk_buff *p; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (msg && (msg[0] != '\0')) 3038c2ecf20Sopenharmony_ci pr_debug("%s:\n", msg); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci for (p = p0; p; p = p->next) 3068c2ecf20Sopenharmony_ci print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_prpkt); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_civoid brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct va_format vaf; 3138c2ecf20Sopenharmony_ci va_list args; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci va_start(args, fmt); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci vaf.fmt = fmt; 3188c2ecf20Sopenharmony_ci vaf.va = &args; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci pr_debug("%pV", &vaf); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci va_end(args); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(brcmu_dbg_hex_dump); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci#endif /* defined(DEBUG) */ 329