utils.c revision b0551fb7e01d76165367ce77ddfcb80009b31427
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		  bool (*fn)(struct sk_buff *, void *), 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}
250EXPORT_SYMBOL(brcmu_pktq_pflush);
251
252void brcmu_pktq_flush(struct pktq *pq, bool dir,
253		      bool (*fn)(struct sk_buff *, void *), void *arg)
254{
255	int prec;
256	for (prec = 0; prec < pq->num_prec; prec++)
257		brcmu_pktq_pflush(pq, prec, dir, fn, arg);
258}
259EXPORT_SYMBOL(brcmu_pktq_flush);
260
261void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len)
262{
263	int prec;
264
265	/* pq is variable size; only zero out what's requested */
266	memset(pq, 0,
267	      offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
268
269	pq->num_prec = (u16) num_prec;
270
271	pq->max = (u16) max_len;
272
273	for (prec = 0; prec < num_prec; prec++)
274		pq->q[prec].max = pq->max;
275}
276EXPORT_SYMBOL(brcmu_pktq_init);
277
278struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out)
279{
280	int prec;
281
282	if (pq->len == 0)
283		return NULL;
284
285	for (prec = 0; prec < pq->hi_prec; prec++)
286		if (pq->q[prec].head)
287			break;
288
289	if (prec_out)
290		*prec_out = prec;
291
292	return pq->q[prec].tail;
293}
294EXPORT_SYMBOL(brcmu_pktq_peek_tail);
295
296/* Return sum of lengths of a specific set of precedences */
297int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp)
298{
299	int prec, len;
300
301	len = 0;
302
303	for (prec = 0; prec <= pq->hi_prec; prec++)
304		if (prec_bmp & (1 << prec))
305			len += pq->q[prec].len;
306
307	return len;
308}
309EXPORT_SYMBOL(brcmu_pktq_mlen);
310
311/* Priority dequeue from a specific set of precedences */
312struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp,
313				      int *prec_out)
314{
315	struct pktq_prec *q;
316	struct sk_buff *p;
317	int prec;
318
319	if (pq->len == 0)
320		return NULL;
321
322	while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
323		pq->hi_prec--;
324
325	while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)
326		if (prec-- == 0)
327			return NULL;
328
329	q = &pq->q[prec];
330
331	p = q->head;
332	if (p == NULL)
333		return NULL;
334
335	q->head = p->prev;
336	if (q->head == NULL)
337		q->tail = NULL;
338
339	q->len--;
340
341	if (prec_out)
342		*prec_out = prec;
343
344	pq->len--;
345
346	p->prev = NULL;
347
348	return p;
349}
350EXPORT_SYMBOL(brcmu_pktq_mdeq);
351
352#if defined(BCMDBG)
353/* pretty hex print a pkt buffer chain */
354void brcmu_prpkt(const char *msg, struct sk_buff *p0)
355{
356	struct sk_buff *p;
357
358	if (msg && (msg[0] != '\0'))
359		printk(KERN_DEBUG "%s:\n", msg);
360
361	for (p = p0; p; p = p->next)
362		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len);
363}
364EXPORT_SYMBOL(brcmu_prpkt);
365#endif				/* defined(BCMDBG) */
366
367/*
368 * Traverse a string of 1-byte tag/1-byte length/variable-length value
369 * triples, returning a pointer to the substring whose first element
370 * matches tag
371 */
372struct brcmu_tlv *brcmu_parse_tlvs(void *buf, int buflen, uint key)
373{
374	struct brcmu_tlv *elt;
375	int totlen;
376
377	elt = (struct brcmu_tlv *) buf;
378	totlen = buflen;
379
380	/* find tagged parameter */
381	while (totlen >= 2) {
382		int len = elt->len;
383
384		/* validate remaining totlen */
385		if ((elt->id == key) && (totlen >= (len + 2)))
386			return elt;
387
388		elt = (struct brcmu_tlv *) ((u8 *) elt + (len + 2));
389		totlen -= (len + 2);
390	}
391
392	return NULL;
393}
394EXPORT_SYMBOL(brcmu_parse_tlvs);
395
396
397#if defined(BCMDBG)
398int
399brcmu_format_flags(const struct brcmu_bit_desc *bd, u32 flags, char *buf,
400		   int len)
401{
402	int i;
403	char *p = buf;
404	char hexstr[16];
405	int slen = 0, nlen = 0;
406	u32 bit;
407	const char *name;
408
409	if (len < 2 || !buf)
410		return 0;
411
412	buf[0] = '\0';
413
414	for (i = 0; flags != 0; i++) {
415		bit = bd[i].bit;
416		name = bd[i].name;
417		if (bit == 0 && flags != 0) {
418			/* print any unnamed bits */
419			snprintf(hexstr, 16, "0x%X", flags);
420			name = hexstr;
421			flags = 0;	/* exit loop */
422		} else if ((flags & bit) == 0)
423			continue;
424		flags &= ~bit;
425		nlen = strlen(name);
426		slen += nlen;
427		/* count btwn flag space */
428		if (flags != 0)
429			slen += 1;
430		/* need NULL char as well */
431		if (len <= slen)
432			break;
433		/* copy NULL char but don't count it */
434		strncpy(p, name, nlen + 1);
435		p += nlen;
436		/* copy btwn flag space and NULL char */
437		if (flags != 0)
438			p += snprintf(p, 2, " ");
439		len -= slen;
440	}
441
442	/* indicate the str was too short */
443	if (flags != 0) {
444		if (len < 2)
445			p -= 2 - len;	/* overwrite last char */
446		p += snprintf(p, 2, ">");
447	}
448
449	return (int)(p - buf);
450}
451EXPORT_SYMBOL(brcmu_format_flags);
452
453/*
454 * print bytes formatted as hex to a string. return the resulting
455 * string length
456 */
457int brcmu_format_hex(char *str, const void *bytes, int len)
458{
459	int i;
460	char *p = str;
461	const u8 *src = (const u8 *)bytes;
462
463	for (i = 0; i < len; i++) {
464		p += snprintf(p, 3, "%02X", *src);
465		src++;
466	}
467	return (int)(p - str);
468}
469EXPORT_SYMBOL(brcmu_format_hex);
470#endif				/* defined(BCMDBG) */
471
472uint brcmu_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)
473{
474	uint len;
475
476	len = strlen(name) + 1;
477
478	if ((len + datalen) > buflen)
479		return 0;
480
481	strncpy(buf, name, buflen);
482
483	/* append data onto the end of the name string */
484	memcpy(&buf[len], data, datalen);
485	len += datalen;
486
487	return len;
488}
489EXPORT_SYMBOL(brcmu_mkiovar);
490
491