Linux 3.9-rc8
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / brcm80211 / brcmutil / utils.c
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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19 #include <linux/netdevice.h>
20 #include <linux/module.h>
21
22 #include <brcmu_utils.h>
23
24 MODULE_AUTHOR("Broadcom Corporation");
25 MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities.");
26 MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
27 MODULE_LICENSE("Dual BSD/GPL");
28
29 struct sk_buff *brcmu_pkt_buf_get_skb(uint len)
30 {
31         struct sk_buff *skb;
32
33         skb = dev_alloc_skb(len);
34         if (skb) {
35                 skb_put(skb, len);
36                 skb->priority = 0;
37         }
38
39         return skb;
40 }
41 EXPORT_SYMBOL(brcmu_pkt_buf_get_skb);
42
43 /* Free the driver packet. Free the tag if present */
44 void brcmu_pkt_buf_free_skb(struct sk_buff *skb)
45 {
46         if (!skb)
47                 return;
48         WARN_ON(skb->next);
49         if (skb->destructor)
50                 /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if
51                  * destructor exists
52                  */
53                 dev_kfree_skb_any(skb);
54         else
55                 /* can free immediately (even in_irq()) if destructor
56                  * does not exist
57                  */
58                 dev_kfree_skb(skb);
59 }
60 EXPORT_SYMBOL(brcmu_pkt_buf_free_skb);
61
62 /*
63  * osl multiple-precedence packet queue
64  * hi_prec is always >= the number of the highest non-empty precedence
65  */
66 struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec,
67                                       struct sk_buff *p)
68 {
69         struct sk_buff_head *q;
70
71         if (pktq_full(pq) || pktq_pfull(pq, prec))
72                 return NULL;
73
74         q = &pq->q[prec].skblist;
75         skb_queue_tail(q, p);
76         pq->len++;
77
78         if (pq->hi_prec < prec)
79                 pq->hi_prec = (u8) prec;
80
81         return p;
82 }
83 EXPORT_SYMBOL(brcmu_pktq_penq);
84
85 struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
86                                            struct sk_buff *p)
87 {
88         struct sk_buff_head *q;
89
90         if (pktq_full(pq) || pktq_pfull(pq, prec))
91                 return NULL;
92
93         q = &pq->q[prec].skblist;
94         skb_queue_head(q, p);
95         pq->len++;
96
97         if (pq->hi_prec < prec)
98                 pq->hi_prec = (u8) prec;
99
100         return p;
101 }
102 EXPORT_SYMBOL(brcmu_pktq_penq_head);
103
104 struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec)
105 {
106         struct sk_buff_head *q;
107         struct sk_buff *p;
108
109         q = &pq->q[prec].skblist;
110         p = skb_dequeue(q);
111         if (p == NULL)
112                 return NULL;
113
114         pq->len--;
115         return p;
116 }
117 EXPORT_SYMBOL(brcmu_pktq_pdeq);
118
119 struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec)
120 {
121         struct sk_buff_head *q;
122         struct sk_buff *p;
123
124         q = &pq->q[prec].skblist;
125         p = skb_dequeue_tail(q);
126         if (p == NULL)
127                 return NULL;
128
129         pq->len--;
130         return p;
131 }
132 EXPORT_SYMBOL(brcmu_pktq_pdeq_tail);
133
134 void
135 brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir,
136                   bool (*fn)(struct sk_buff *, void *), void *arg)
137 {
138         struct sk_buff_head *q;
139         struct sk_buff *p, *next;
140
141         q = &pq->q[prec].skblist;
142         skb_queue_walk_safe(q, p, next) {
143                 if (fn == NULL || (*fn) (p, arg)) {
144                         skb_unlink(p, q);
145                         brcmu_pkt_buf_free_skb(p);
146                         pq->len--;
147                 }
148         }
149 }
150 EXPORT_SYMBOL(brcmu_pktq_pflush);
151
152 void brcmu_pktq_flush(struct pktq *pq, bool dir,
153                       bool (*fn)(struct sk_buff *, void *), void *arg)
154 {
155         int prec;
156         for (prec = 0; prec < pq->num_prec; prec++)
157                 brcmu_pktq_pflush(pq, prec, dir, fn, arg);
158 }
159 EXPORT_SYMBOL(brcmu_pktq_flush);
160
161 void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len)
162 {
163         int prec;
164
165         /* pq is variable size; only zero out what's requested */
166         memset(pq, 0,
167               offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
168
169         pq->num_prec = (u16) num_prec;
170
171         pq->max = (u16) max_len;
172
173         for (prec = 0; prec < num_prec; prec++) {
174                 pq->q[prec].max = pq->max;
175                 skb_queue_head_init(&pq->q[prec].skblist);
176         }
177 }
178 EXPORT_SYMBOL(brcmu_pktq_init);
179
180 struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out)
181 {
182         int prec;
183
184         if (pq->len == 0)
185                 return NULL;
186
187         for (prec = 0; prec < pq->hi_prec; prec++)
188                 if (!skb_queue_empty(&pq->q[prec].skblist))
189                         break;
190
191         if (prec_out)
192                 *prec_out = prec;
193
194         return skb_peek_tail(&pq->q[prec].skblist);
195 }
196 EXPORT_SYMBOL(brcmu_pktq_peek_tail);
197
198 /* Return sum of lengths of a specific set of precedences */
199 int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp)
200 {
201         int prec, len;
202
203         len = 0;
204
205         for (prec = 0; prec <= pq->hi_prec; prec++)
206                 if (prec_bmp & (1 << prec))
207                         len += pq->q[prec].skblist.qlen;
208
209         return len;
210 }
211 EXPORT_SYMBOL(brcmu_pktq_mlen);
212
213 /* Priority dequeue from a specific set of precedences */
214 struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp,
215                                       int *prec_out)
216 {
217         struct sk_buff_head *q;
218         struct sk_buff *p;
219         int prec;
220
221         if (pq->len == 0)
222                 return NULL;
223
224         while ((prec = pq->hi_prec) > 0 &&
225                skb_queue_empty(&pq->q[prec].skblist))
226                 pq->hi_prec--;
227
228         while ((prec_bmp & (1 << prec)) == 0 ||
229                skb_queue_empty(&pq->q[prec].skblist))
230                 if (prec-- == 0)
231                         return NULL;
232
233         q = &pq->q[prec].skblist;
234         p = skb_dequeue(q);
235         if (p == NULL)
236                 return NULL;
237
238         pq->len--;
239
240         if (prec_out)
241                 *prec_out = prec;
242
243         return p;
244 }
245 EXPORT_SYMBOL(brcmu_pktq_mdeq);
246
247 #if defined(DEBUG)
248 /* pretty hex print a pkt buffer chain */
249 void brcmu_prpkt(const char *msg, struct sk_buff *p0)
250 {
251         struct sk_buff *p;
252
253         if (msg && (msg[0] != '\0'))
254                 pr_debug("%s:\n", msg);
255
256         for (p = p0; p; p = p->next)
257                 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len);
258 }
259 EXPORT_SYMBOL(brcmu_prpkt);
260
261 void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...)
262 {
263         struct va_format vaf;
264         va_list args;
265
266         va_start(args, fmt);
267
268         vaf.fmt = fmt;
269         vaf.va = &args;
270
271         pr_debug("%pV", &vaf);
272
273         va_end(args);
274
275         print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size);
276 }
277 EXPORT_SYMBOL(brcmu_dbg_hex_dump);
278 #endif                          /* defined(DEBUG) */