blob: 57e656f987adbb7deb2a1e84a2cb5c3e26626f08 [file] [log] [blame]
Arend van Spriel5b435de2011-10-05 13:19:03 +02001/*
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>
Stephen Rothwellb7a57e72011-10-12 21:35:07 +020018#include <linux/module.h>
Arend van Spriel20e5ca12011-10-18 14:03:09 +020019
Arend van Spriel5b435de2011-10-05 13:19:03 +020020#include <brcmu_utils.h>
21
22MODULE_AUTHOR("Broadcom Corporation");
23MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities.");
24MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
25MODULE_LICENSE("Dual BSD/GPL");
26
27struct sk_buff *brcmu_pkt_buf_get_skb(uint len)
28{
29 struct sk_buff *skb;
30
31 skb = dev_alloc_skb(len);
32 if (skb) {
33 skb_put(skb, len);
34 skb->priority = 0;
35 }
36
37 return skb;
38}
39EXPORT_SYMBOL(brcmu_pkt_buf_get_skb);
40
41/* Free the driver packet. Free the tag if present */
42void brcmu_pkt_buf_free_skb(struct sk_buff *skb)
43{
44 struct sk_buff *nskb;
45 int nest = 0;
46
47 /* perversion: we use skb->next to chain multi-skb packets */
48 while (skb) {
49 nskb = skb->next;
50 skb->next = NULL;
51
52 if (skb->destructor)
53 /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if
54 * destructor exists
55 */
56 dev_kfree_skb_any(skb);
57 else
58 /* can free immediately (even in_irq()) if destructor
59 * does not exist
60 */
61 dev_kfree_skb(skb);
62
63 nest++;
64 skb = nskb;
65 }
66}
67EXPORT_SYMBOL(brcmu_pkt_buf_free_skb);
68
69
Arend van Spriel5b435de2011-10-05 13:19:03 +020070/* return total length of buffer chain */
71uint brcmu_pkttotlen(struct sk_buff *p)
72{
73 uint total;
74
75 total = 0;
76 for (; p; p = p->next)
77 total += p->len;
78 return total;
79}
80EXPORT_SYMBOL(brcmu_pkttotlen);
81
82/*
83 * osl multiple-precedence packet queue
84 * hi_prec is always >= the number of the highest non-empty precedence
85 */
86struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec,
87 struct sk_buff *p)
88{
Arend van Sprielad3b8b32011-11-10 20:30:27 +010089 struct sk_buff_head *q;
Arend van Spriel5b435de2011-10-05 13:19:03 +020090
91 if (pktq_full(pq) || pktq_pfull(pq, prec))
92 return NULL;
93
Arend van Sprielad3b8b32011-11-10 20:30:27 +010094 q = &pq->q[prec].skblist;
95 skb_queue_tail(q, p);
Arend van Spriel5b435de2011-10-05 13:19:03 +020096 pq->len++;
97
98 if (pq->hi_prec < prec)
99 pq->hi_prec = (u8) prec;
100
101 return p;
102}
103EXPORT_SYMBOL(brcmu_pktq_penq);
104
105struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
106 struct sk_buff *p)
107{
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100108 struct sk_buff_head *q;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200109
110 if (pktq_full(pq) || pktq_pfull(pq, prec))
111 return NULL;
112
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100113 q = &pq->q[prec].skblist;
114 skb_queue_head(q, p);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200115 pq->len++;
116
117 if (pq->hi_prec < prec)
118 pq->hi_prec = (u8) prec;
119
120 return p;
121}
122EXPORT_SYMBOL(brcmu_pktq_penq_head);
123
124struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec)
125{
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100126 struct sk_buff_head *q;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200127 struct sk_buff *p;
128
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100129 q = &pq->q[prec].skblist;
130 p = skb_dequeue(q);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200131 if (p == NULL)
132 return NULL;
133
Arend van Spriel5b435de2011-10-05 13:19:03 +0200134 pq->len--;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200135 return p;
136}
137EXPORT_SYMBOL(brcmu_pktq_pdeq);
138
139struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec)
140{
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100141 struct sk_buff_head *q;
142 struct sk_buff *p;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200143
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100144 q = &pq->q[prec].skblist;
145 p = skb_dequeue_tail(q);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200146 if (p == NULL)
147 return NULL;
148
Arend van Spriel5b435de2011-10-05 13:19:03 +0200149 pq->len--;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200150 return p;
151}
152EXPORT_SYMBOL(brcmu_pktq_pdeq_tail);
153
154void
155brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir,
156 bool (*fn)(struct sk_buff *, void *), void *arg)
157{
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100158 struct sk_buff_head *q;
159 struct sk_buff *p, *next;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200160
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100161 q = &pq->q[prec].skblist;
162 skb_queue_walk_safe(q, p, next) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200163 if (fn == NULL || (*fn) (p, arg)) {
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100164 skb_unlink(p, q);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200165 brcmu_pkt_buf_free_skb(p);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200166 pq->len--;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200167 }
168 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200169}
170EXPORT_SYMBOL(brcmu_pktq_pflush);
171
172void brcmu_pktq_flush(struct pktq *pq, bool dir,
173 bool (*fn)(struct sk_buff *, void *), void *arg)
174{
175 int prec;
176 for (prec = 0; prec < pq->num_prec; prec++)
177 brcmu_pktq_pflush(pq, prec, dir, fn, arg);
178}
179EXPORT_SYMBOL(brcmu_pktq_flush);
180
181void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len)
182{
183 int prec;
184
185 /* pq is variable size; only zero out what's requested */
186 memset(pq, 0,
187 offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
188
189 pq->num_prec = (u16) num_prec;
190
191 pq->max = (u16) max_len;
192
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100193 for (prec = 0; prec < num_prec; prec++) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200194 pq->q[prec].max = pq->max;
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100195 skb_queue_head_init(&pq->q[prec].skblist);
196 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200197}
198EXPORT_SYMBOL(brcmu_pktq_init);
199
200struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out)
201{
202 int prec;
203
204 if (pq->len == 0)
205 return NULL;
206
207 for (prec = 0; prec < pq->hi_prec; prec++)
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100208 if (!skb_queue_empty(&pq->q[prec].skblist))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200209 break;
210
211 if (prec_out)
212 *prec_out = prec;
213
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100214 return skb_peek_tail(&pq->q[prec].skblist);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200215}
216EXPORT_SYMBOL(brcmu_pktq_peek_tail);
217
218/* Return sum of lengths of a specific set of precedences */
219int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp)
220{
221 int prec, len;
222
223 len = 0;
224
225 for (prec = 0; prec <= pq->hi_prec; prec++)
226 if (prec_bmp & (1 << prec))
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100227 len += pq->q[prec].skblist.qlen;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200228
229 return len;
230}
231EXPORT_SYMBOL(brcmu_pktq_mlen);
232
233/* Priority dequeue from a specific set of precedences */
234struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp,
235 int *prec_out)
236{
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100237 struct sk_buff_head *q;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200238 struct sk_buff *p;
239 int prec;
240
241 if (pq->len == 0)
242 return NULL;
243
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100244 while ((prec = pq->hi_prec) > 0 &&
245 skb_queue_empty(&pq->q[prec].skblist))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200246 pq->hi_prec--;
247
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100248 while ((prec_bmp & (1 << prec)) == 0 ||
249 skb_queue_empty(&pq->q[prec].skblist))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200250 if (prec-- == 0)
251 return NULL;
252
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100253 q = &pq->q[prec].skblist;
254 p = skb_dequeue(q);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200255 if (p == NULL)
256 return NULL;
257
Arend van Sprielad3b8b32011-11-10 20:30:27 +0100258 pq->len--;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200259
260 if (prec_out)
261 *prec_out = prec;
262
Arend van Spriel5b435de2011-10-05 13:19:03 +0200263 return p;
264}
265EXPORT_SYMBOL(brcmu_pktq_mdeq);
266
267#if defined(BCMDBG)
268/* pretty hex print a pkt buffer chain */
269void brcmu_prpkt(const char *msg, struct sk_buff *p0)
270{
271 struct sk_buff *p;
272
273 if (msg && (msg[0] != '\0'))
274 printk(KERN_DEBUG "%s:\n", msg);
275
276 for (p = p0; p; p = p->next)
277 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len);
278}
279EXPORT_SYMBOL(brcmu_prpkt);
280#endif /* defined(BCMDBG) */