xref: /linux/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
1afe06f82SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo  * Copyright (c) 2010 Broadcom Corporation
405491d2cSKalle Valo  */
505491d2cSKalle Valo 
605491d2cSKalle Valo #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
705491d2cSKalle Valo 
805491d2cSKalle Valo #include <linux/netdevice.h>
905491d2cSKalle Valo #include <linux/module.h>
1005491d2cSKalle Valo 
1105491d2cSKalle Valo #include <brcmu_utils.h>
1205491d2cSKalle Valo 
1305491d2cSKalle Valo MODULE_AUTHOR("Broadcom Corporation");
1405491d2cSKalle Valo MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities.");
1505491d2cSKalle Valo MODULE_LICENSE("Dual BSD/GPL");
1605491d2cSKalle Valo 
brcmu_pkt_buf_get_skb(uint len)1705491d2cSKalle Valo struct sk_buff *brcmu_pkt_buf_get_skb(uint len)
1805491d2cSKalle Valo {
1905491d2cSKalle Valo 	struct sk_buff *skb;
2005491d2cSKalle Valo 
2105491d2cSKalle Valo 	skb = dev_alloc_skb(len);
2205491d2cSKalle Valo 	if (skb) {
2305491d2cSKalle Valo 		skb_put(skb, len);
2405491d2cSKalle Valo 		skb->priority = 0;
2505491d2cSKalle Valo 	}
2605491d2cSKalle Valo 
2705491d2cSKalle Valo 	return skb;
2805491d2cSKalle Valo }
2905491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pkt_buf_get_skb);
3005491d2cSKalle Valo 
3105491d2cSKalle Valo /* Free the driver packet. Free the tag if present */
brcmu_pkt_buf_free_skb(struct sk_buff * skb)3205491d2cSKalle Valo void brcmu_pkt_buf_free_skb(struct sk_buff *skb)
3305491d2cSKalle Valo {
3405491d2cSKalle Valo 	if (!skb)
3505491d2cSKalle Valo 		return;
3605491d2cSKalle Valo 
3705491d2cSKalle Valo 	WARN_ON(skb->next);
3805491d2cSKalle Valo 	dev_kfree_skb_any(skb);
3905491d2cSKalle Valo }
4005491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pkt_buf_free_skb);
4105491d2cSKalle Valo 
4205491d2cSKalle Valo /*
4305491d2cSKalle Valo  * osl multiple-precedence packet queue
4405491d2cSKalle Valo  * hi_prec is always >= the number of the highest non-empty precedence
4505491d2cSKalle Valo  */
brcmu_pktq_penq(struct pktq * pq,int prec,struct sk_buff * p)4605491d2cSKalle Valo struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec,
4705491d2cSKalle Valo 				      struct sk_buff *p)
4805491d2cSKalle Valo {
4905491d2cSKalle Valo 	struct sk_buff_head *q;
5005491d2cSKalle Valo 
5105491d2cSKalle Valo 	if (pktq_full(pq) || pktq_pfull(pq, prec))
5205491d2cSKalle Valo 		return NULL;
5305491d2cSKalle Valo 
5405491d2cSKalle Valo 	q = &pq->q[prec].skblist;
5505491d2cSKalle Valo 	skb_queue_tail(q, p);
5605491d2cSKalle Valo 	pq->len++;
5705491d2cSKalle Valo 
5805491d2cSKalle Valo 	if (pq->hi_prec < prec)
5905491d2cSKalle Valo 		pq->hi_prec = (u8) prec;
6005491d2cSKalle Valo 
6105491d2cSKalle Valo 	return p;
6205491d2cSKalle Valo }
6305491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_penq);
6405491d2cSKalle Valo 
brcmu_pktq_penq_head(struct pktq * pq,int prec,struct sk_buff * p)6505491d2cSKalle Valo struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
6605491d2cSKalle Valo 					   struct sk_buff *p)
6705491d2cSKalle Valo {
6805491d2cSKalle Valo 	struct sk_buff_head *q;
6905491d2cSKalle Valo 
7005491d2cSKalle Valo 	if (pktq_full(pq) || pktq_pfull(pq, prec))
7105491d2cSKalle Valo 		return NULL;
7205491d2cSKalle Valo 
7305491d2cSKalle Valo 	q = &pq->q[prec].skblist;
7405491d2cSKalle Valo 	skb_queue_head(q, p);
7505491d2cSKalle Valo 	pq->len++;
7605491d2cSKalle Valo 
7705491d2cSKalle Valo 	if (pq->hi_prec < prec)
7805491d2cSKalle Valo 		pq->hi_prec = (u8) prec;
7905491d2cSKalle Valo 
8005491d2cSKalle Valo 	return p;
8105491d2cSKalle Valo }
8205491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_penq_head);
8305491d2cSKalle Valo 
brcmu_pktq_pdeq(struct pktq * pq,int prec)8405491d2cSKalle Valo struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec)
8505491d2cSKalle Valo {
8605491d2cSKalle Valo 	struct sk_buff_head *q;
8705491d2cSKalle Valo 	struct sk_buff *p;
8805491d2cSKalle Valo 
8905491d2cSKalle Valo 	q = &pq->q[prec].skblist;
9005491d2cSKalle Valo 	p = skb_dequeue(q);
9105491d2cSKalle Valo 	if (p == NULL)
9205491d2cSKalle Valo 		return NULL;
9305491d2cSKalle Valo 
9405491d2cSKalle Valo 	pq->len--;
9505491d2cSKalle Valo 	return p;
9605491d2cSKalle Valo }
9705491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_pdeq);
9805491d2cSKalle Valo 
9905491d2cSKalle Valo /*
10005491d2cSKalle Valo  * precedence based dequeue with match function. Passing a NULL pointer
10105491d2cSKalle Valo  * for the match function parameter is considered to be a wildcard so
10205491d2cSKalle Valo  * any packet on the queue is returned. In that case it is no different
10305491d2cSKalle Valo  * from brcmu_pktq_pdeq() above.
10405491d2cSKalle Valo  */
brcmu_pktq_pdeq_match(struct pktq * pq,int prec,bool (* match_fn)(struct sk_buff * skb,void * arg),void * arg)10505491d2cSKalle Valo struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec,
10605491d2cSKalle Valo 				      bool (*match_fn)(struct sk_buff *skb,
10705491d2cSKalle Valo 						       void *arg), void *arg)
10805491d2cSKalle Valo {
10905491d2cSKalle Valo 	struct sk_buff_head *q;
11005491d2cSKalle Valo 	struct sk_buff *p, *next;
11105491d2cSKalle Valo 
11205491d2cSKalle Valo 	q = &pq->q[prec].skblist;
11305491d2cSKalle Valo 	skb_queue_walk_safe(q, p, next) {
11405491d2cSKalle Valo 		if (match_fn == NULL || match_fn(p, arg)) {
11505491d2cSKalle Valo 			skb_unlink(p, q);
11605491d2cSKalle Valo 			pq->len--;
11705491d2cSKalle Valo 			return p;
11805491d2cSKalle Valo 		}
11905491d2cSKalle Valo 	}
12005491d2cSKalle Valo 	return NULL;
12105491d2cSKalle Valo }
12205491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_pdeq_match);
12305491d2cSKalle Valo 
brcmu_pktq_pdeq_tail(struct pktq * pq,int prec)12405491d2cSKalle Valo struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec)
12505491d2cSKalle Valo {
12605491d2cSKalle Valo 	struct sk_buff_head *q;
12705491d2cSKalle Valo 	struct sk_buff *p;
12805491d2cSKalle Valo 
12905491d2cSKalle Valo 	q = &pq->q[prec].skblist;
13005491d2cSKalle Valo 	p = skb_dequeue_tail(q);
13105491d2cSKalle Valo 	if (p == NULL)
13205491d2cSKalle Valo 		return NULL;
13305491d2cSKalle Valo 
13405491d2cSKalle Valo 	pq->len--;
13505491d2cSKalle Valo 	return p;
13605491d2cSKalle Valo }
13705491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_pdeq_tail);
13805491d2cSKalle Valo 
13905491d2cSKalle Valo void
brcmu_pktq_pflush(struct pktq * pq,int prec,bool dir,bool (* fn)(struct sk_buff *,void *),void * arg)14005491d2cSKalle Valo brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir,
14105491d2cSKalle Valo 		  bool (*fn)(struct sk_buff *, void *), void *arg)
14205491d2cSKalle Valo {
14305491d2cSKalle Valo 	struct sk_buff_head *q;
14405491d2cSKalle Valo 	struct sk_buff *p, *next;
14505491d2cSKalle Valo 
14605491d2cSKalle Valo 	q = &pq->q[prec].skblist;
14705491d2cSKalle Valo 	skb_queue_walk_safe(q, p, next) {
14805491d2cSKalle Valo 		if (fn == NULL || (*fn) (p, arg)) {
14905491d2cSKalle Valo 			skb_unlink(p, q);
15005491d2cSKalle Valo 			brcmu_pkt_buf_free_skb(p);
15105491d2cSKalle Valo 			pq->len--;
15205491d2cSKalle Valo 		}
15305491d2cSKalle Valo 	}
15405491d2cSKalle Valo }
15505491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_pflush);
15605491d2cSKalle Valo 
brcmu_pktq_flush(struct pktq * pq,bool dir,bool (* fn)(struct sk_buff *,void *),void * arg)15705491d2cSKalle Valo void brcmu_pktq_flush(struct pktq *pq, bool dir,
15805491d2cSKalle Valo 		      bool (*fn)(struct sk_buff *, void *), void *arg)
15905491d2cSKalle Valo {
16005491d2cSKalle Valo 	int prec;
16105491d2cSKalle Valo 	for (prec = 0; prec < pq->num_prec; prec++)
16205491d2cSKalle Valo 		brcmu_pktq_pflush(pq, prec, dir, fn, arg);
16305491d2cSKalle Valo }
16405491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_flush);
16505491d2cSKalle Valo 
brcmu_pktq_init(struct pktq * pq,int num_prec,int max_len)16605491d2cSKalle Valo void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len)
16705491d2cSKalle Valo {
16805491d2cSKalle Valo 	int prec;
16905491d2cSKalle Valo 
17005491d2cSKalle Valo 	/* pq is variable size; only zero out what's requested */
17105491d2cSKalle Valo 	memset(pq, 0,
17205491d2cSKalle Valo 	      offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
17305491d2cSKalle Valo 
17405491d2cSKalle Valo 	pq->num_prec = (u16) num_prec;
17505491d2cSKalle Valo 
17605491d2cSKalle Valo 	pq->max = (u16) max_len;
17705491d2cSKalle Valo 
17805491d2cSKalle Valo 	for (prec = 0; prec < num_prec; prec++) {
17905491d2cSKalle Valo 		pq->q[prec].max = pq->max;
18005491d2cSKalle Valo 		skb_queue_head_init(&pq->q[prec].skblist);
18105491d2cSKalle Valo 	}
18205491d2cSKalle Valo }
18305491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_init);
18405491d2cSKalle Valo 
brcmu_pktq_peek_tail(struct pktq * pq,int * prec_out)18505491d2cSKalle Valo struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out)
18605491d2cSKalle Valo {
18705491d2cSKalle Valo 	int prec;
18805491d2cSKalle Valo 
189*8012ec4aSJuhee Kang 	if (pktq_empty(pq))
19005491d2cSKalle Valo 		return NULL;
19105491d2cSKalle Valo 
19205491d2cSKalle Valo 	for (prec = 0; prec < pq->hi_prec; prec++)
19305491d2cSKalle Valo 		if (!skb_queue_empty(&pq->q[prec].skblist))
19405491d2cSKalle Valo 			break;
19505491d2cSKalle Valo 
19605491d2cSKalle Valo 	if (prec_out)
19705491d2cSKalle Valo 		*prec_out = prec;
19805491d2cSKalle Valo 
19905491d2cSKalle Valo 	return skb_peek_tail(&pq->q[prec].skblist);
20005491d2cSKalle Valo }
20105491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_peek_tail);
20205491d2cSKalle Valo 
20305491d2cSKalle Valo /* Return sum of lengths of a specific set of precedences */
brcmu_pktq_mlen(struct pktq * pq,uint prec_bmp)20405491d2cSKalle Valo int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp)
20505491d2cSKalle Valo {
20605491d2cSKalle Valo 	int prec, len;
20705491d2cSKalle Valo 
20805491d2cSKalle Valo 	len = 0;
20905491d2cSKalle Valo 
21005491d2cSKalle Valo 	for (prec = 0; prec <= pq->hi_prec; prec++)
21105491d2cSKalle Valo 		if (prec_bmp & (1 << prec))
21205491d2cSKalle Valo 			len += pq->q[prec].skblist.qlen;
21305491d2cSKalle Valo 
21405491d2cSKalle Valo 	return len;
21505491d2cSKalle Valo }
21605491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_mlen);
21705491d2cSKalle Valo 
21805491d2cSKalle Valo /* Priority dequeue from a specific set of precedences */
brcmu_pktq_mdeq(struct pktq * pq,uint prec_bmp,int * prec_out)21905491d2cSKalle Valo struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp,
22005491d2cSKalle Valo 				      int *prec_out)
22105491d2cSKalle Valo {
22205491d2cSKalle Valo 	struct sk_buff_head *q;
22305491d2cSKalle Valo 	struct sk_buff *p;
22405491d2cSKalle Valo 	int prec;
22505491d2cSKalle Valo 
226*8012ec4aSJuhee Kang 	if (pktq_empty(pq))
22705491d2cSKalle Valo 		return NULL;
22805491d2cSKalle Valo 
22905491d2cSKalle Valo 	while ((prec = pq->hi_prec) > 0 &&
23005491d2cSKalle Valo 	       skb_queue_empty(&pq->q[prec].skblist))
23105491d2cSKalle Valo 		pq->hi_prec--;
23205491d2cSKalle Valo 
23305491d2cSKalle Valo 	while ((prec_bmp & (1 << prec)) == 0 ||
23405491d2cSKalle Valo 	       skb_queue_empty(&pq->q[prec].skblist))
23505491d2cSKalle Valo 		if (prec-- == 0)
23605491d2cSKalle Valo 			return NULL;
23705491d2cSKalle Valo 
23805491d2cSKalle Valo 	q = &pq->q[prec].skblist;
23905491d2cSKalle Valo 	p = skb_dequeue(q);
24005491d2cSKalle Valo 	if (p == NULL)
24105491d2cSKalle Valo 		return NULL;
24205491d2cSKalle Valo 
24305491d2cSKalle Valo 	pq->len--;
24405491d2cSKalle Valo 
24505491d2cSKalle Valo 	if (prec_out)
24605491d2cSKalle Valo 		*prec_out = prec;
24705491d2cSKalle Valo 
24805491d2cSKalle Valo 	return p;
24905491d2cSKalle Valo }
25005491d2cSKalle Valo EXPORT_SYMBOL(brcmu_pktq_mdeq);
25105491d2cSKalle Valo 
25205491d2cSKalle Valo /* Produce a human-readable string for boardrev */
brcmu_boardrev_str(u32 brev,char * buf)25305491d2cSKalle Valo char *brcmu_boardrev_str(u32 brev, char *buf)
25405491d2cSKalle Valo {
25505491d2cSKalle Valo 	char c;
25605491d2cSKalle Valo 
25705491d2cSKalle Valo 	if (brev < 0x100) {
25805491d2cSKalle Valo 		snprintf(buf, BRCMU_BOARDREV_LEN, "%d.%d",
25905491d2cSKalle Valo 			 (brev & 0xf0) >> 4, brev & 0xf);
26005491d2cSKalle Valo 	} else {
26105491d2cSKalle Valo 		c = (brev & 0xf000) == 0x1000 ? 'P' : 'A';
26205491d2cSKalle Valo 		snprintf(buf, BRCMU_BOARDREV_LEN, "%c%03x", c, brev & 0xfff);
26305491d2cSKalle Valo 	}
26405491d2cSKalle Valo 	return buf;
26505491d2cSKalle Valo }
26605491d2cSKalle Valo EXPORT_SYMBOL(brcmu_boardrev_str);
26705491d2cSKalle Valo 
brcmu_dotrev_str(u32 dotrev,char * buf)26805491d2cSKalle Valo char *brcmu_dotrev_str(u32 dotrev, char *buf)
26905491d2cSKalle Valo {
27005491d2cSKalle Valo 	u8 dotval[4];
27105491d2cSKalle Valo 
27205491d2cSKalle Valo 	if (!dotrev) {
27305491d2cSKalle Valo 		snprintf(buf, BRCMU_DOTREV_LEN, "unknown");
27405491d2cSKalle Valo 		return buf;
27505491d2cSKalle Valo 	}
27605491d2cSKalle Valo 	dotval[0] = (dotrev >> 24) & 0xFF;
27705491d2cSKalle Valo 	dotval[1] = (dotrev >> 16) & 0xFF;
27805491d2cSKalle Valo 	dotval[2] = (dotrev >> 8) & 0xFF;
27905491d2cSKalle Valo 	dotval[3] = dotrev & 0xFF;
28005491d2cSKalle Valo 
28105491d2cSKalle Valo 	if (dotval[3])
28205491d2cSKalle Valo 		snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d.%d", dotval[0],
28305491d2cSKalle Valo 			dotval[1], dotval[2], dotval[3]);
28405491d2cSKalle Valo 	else if (dotval[2])
28505491d2cSKalle Valo 		snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d", dotval[0],
28605491d2cSKalle Valo 			dotval[1], dotval[2]);
28705491d2cSKalle Valo 	else
28805491d2cSKalle Valo 		snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d", dotval[0],
28905491d2cSKalle Valo 			dotval[1]);
29005491d2cSKalle Valo 
29105491d2cSKalle Valo 	return buf;
29205491d2cSKalle Valo }
29305491d2cSKalle Valo EXPORT_SYMBOL(brcmu_dotrev_str);
29405491d2cSKalle Valo 
29505491d2cSKalle Valo #if defined(DEBUG)
29605491d2cSKalle Valo /* pretty hex print a pkt buffer chain */
brcmu_prpkt(const char * msg,struct sk_buff * p0)29705491d2cSKalle Valo void brcmu_prpkt(const char *msg, struct sk_buff *p0)
29805491d2cSKalle Valo {
29905491d2cSKalle Valo 	struct sk_buff *p;
30005491d2cSKalle Valo 
30105491d2cSKalle Valo 	if (msg && (msg[0] != '\0'))
30205491d2cSKalle Valo 		pr_debug("%s:\n", msg);
30305491d2cSKalle Valo 
30405491d2cSKalle Valo 	for (p = p0; p; p = p->next)
30505491d2cSKalle Valo 		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len);
30605491d2cSKalle Valo }
30705491d2cSKalle Valo EXPORT_SYMBOL(brcmu_prpkt);
30805491d2cSKalle Valo 
brcmu_dbg_hex_dump(const void * data,size_t size,const char * fmt,...)30905491d2cSKalle Valo void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...)
31005491d2cSKalle Valo {
31105491d2cSKalle Valo 	struct va_format vaf;
31205491d2cSKalle Valo 	va_list args;
31305491d2cSKalle Valo 
31405491d2cSKalle Valo 	va_start(args, fmt);
31505491d2cSKalle Valo 
31605491d2cSKalle Valo 	vaf.fmt = fmt;
31705491d2cSKalle Valo 	vaf.va = &args;
31805491d2cSKalle Valo 
31905491d2cSKalle Valo 	pr_debug("%pV", &vaf);
32005491d2cSKalle Valo 
32105491d2cSKalle Valo 	va_end(args);
32205491d2cSKalle Valo 
32305491d2cSKalle Valo 	print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size);
32405491d2cSKalle Valo }
32505491d2cSKalle Valo EXPORT_SYMBOL(brcmu_dbg_hex_dump);
32605491d2cSKalle Valo 
32705491d2cSKalle Valo #endif				/* defined(DEBUG) */
328