Commit | Line | Data |
---|---|---|
9a1bb602 HM |
1 | /* Copyright (c) 2014 Broadcom Corporation |
2 | * | |
3 | * Permission to use, copy, modify, and/or distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
10 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | |
12 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
13 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | ||
17 | #include <linux/types.h> | |
18 | #include <linux/netdevice.h> | |
19 | #include <linux/etherdevice.h> | |
20 | #include <brcmu_utils.h> | |
21 | ||
22 | #include "dhd.h" | |
23 | #include "dhd_dbg.h" | |
24 | #include "dhd_bus.h" | |
25 | #include "proto.h" | |
26 | #include "flowring.h" | |
27 | #include "msgbuf.h" | |
28 | ||
29 | ||
30 | #define BRCMF_FLOWRING_HIGH 1024 | |
31 | #define BRCMF_FLOWRING_LOW (BRCMF_FLOWRING_HIGH - 256) | |
32 | #define BRCMF_FLOWRING_INVALID_IFIDX 0xff | |
33 | ||
34 | #define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16) | |
35 | #define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16) | |
36 | ||
37 | static const u8 ALLZEROMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | |
38 | static const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | |
39 | ||
40 | static const u8 brcmf_flowring_prio2fifo[] = { | |
41 | 1, | |
42 | 0, | |
43 | 0, | |
44 | 1, | |
45 | 2, | |
46 | 2, | |
47 | 3, | |
48 | 3 | |
49 | }; | |
50 | ||
51 | ||
52 | u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], | |
53 | u8 prio, u8 ifidx) | |
54 | { | |
55 | struct brcmf_flowring_hash *hash; | |
56 | u8 hash_idx; | |
57 | u32 i; | |
58 | bool found; | |
59 | bool sta; | |
60 | u8 fifo; | |
61 | u8 *mac; | |
62 | ||
63 | fifo = brcmf_flowring_prio2fifo[prio]; | |
64 | sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); | |
65 | mac = da; | |
66 | if ((!sta) && (is_multicast_ether_addr(da))) { | |
67 | mac = (u8 *)ALLFFMAC; | |
68 | fifo = 0; | |
69 | } | |
70 | hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : | |
71 | BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); | |
72 | found = false; | |
73 | hash = flow->hash; | |
74 | for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { | |
75 | if ((sta || (memcmp(hash[hash_idx].mac, mac, ETH_ALEN) == 0)) && | |
76 | (hash[hash_idx].fifo == fifo) && | |
77 | (hash[hash_idx].ifidx == ifidx)) { | |
78 | found = true; | |
79 | break; | |
80 | } | |
81 | hash_idx++; | |
82 | } | |
83 | if (found) | |
84 | return hash[hash_idx].flowid; | |
85 | ||
86 | return BRCMF_FLOWRING_INVALID_ID; | |
87 | } | |
88 | ||
89 | ||
90 | u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], | |
91 | u8 prio, u8 ifidx) | |
92 | { | |
93 | struct brcmf_flowring_ring *ring; | |
94 | struct brcmf_flowring_hash *hash; | |
95 | u8 hash_idx; | |
96 | u32 i; | |
97 | bool found; | |
98 | u8 fifo; | |
99 | bool sta; | |
100 | u8 *mac; | |
101 | ||
102 | fifo = brcmf_flowring_prio2fifo[prio]; | |
103 | sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); | |
104 | mac = da; | |
105 | if ((!sta) && (is_multicast_ether_addr(da))) { | |
106 | mac = (u8 *)ALLFFMAC; | |
107 | fifo = 0; | |
108 | } | |
109 | hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : | |
110 | BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); | |
111 | found = false; | |
112 | hash = flow->hash; | |
113 | for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { | |
114 | if (((sta) && | |
115 | (hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX)) || | |
116 | ((!sta) && | |
117 | (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0))) { | |
118 | found = true; | |
119 | break; | |
120 | } | |
121 | hash_idx++; | |
122 | } | |
123 | if (found) { | |
124 | for (i = 0; i < flow->nrofrings; i++) { | |
125 | if (flow->rings[i] == NULL) | |
126 | break; | |
127 | } | |
128 | if (i == flow->nrofrings) | |
129 | return -ENOMEM; | |
130 | ||
131 | ring = kzalloc(sizeof(*ring), GFP_ATOMIC); | |
132 | if (!ring) | |
133 | return -ENOMEM; | |
134 | ||
135 | memcpy(hash[hash_idx].mac, mac, ETH_ALEN); | |
136 | hash[hash_idx].fifo = fifo; | |
137 | hash[hash_idx].ifidx = ifidx; | |
138 | hash[hash_idx].flowid = i; | |
139 | ||
140 | ring->hash_id = hash_idx; | |
141 | ring->status = RING_CLOSED; | |
142 | skb_queue_head_init(&ring->skblist); | |
143 | flow->rings[i] = ring; | |
144 | ||
145 | return i; | |
146 | } | |
147 | return BRCMF_FLOWRING_INVALID_ID; | |
148 | } | |
149 | ||
150 | ||
151 | u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid) | |
152 | { | |
153 | struct brcmf_flowring_ring *ring; | |
154 | ||
155 | ring = flow->rings[flowid]; | |
156 | ||
157 | return flow->hash[ring->hash_id].fifo; | |
158 | } | |
159 | ||
160 | ||
161 | void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) | |
162 | { | |
163 | struct brcmf_flowring_ring *ring; | |
164 | u8 hash_idx; | |
165 | struct sk_buff *skb; | |
166 | ||
167 | ring = flow->rings[flowid]; | |
168 | if (!ring) | |
169 | return; | |
170 | hash_idx = ring->hash_id; | |
171 | flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; | |
172 | memset(flow->hash[hash_idx].mac, 0, ETH_ALEN); | |
173 | flow->rings[flowid] = NULL; | |
174 | ||
175 | skb = skb_dequeue(&ring->skblist); | |
176 | while (skb) { | |
177 | brcmu_pkt_buf_free_skb(skb); | |
178 | skb = skb_dequeue(&ring->skblist); | |
179 | } | |
180 | ||
181 | kfree(ring); | |
182 | } | |
183 | ||
184 | ||
185 | void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, | |
186 | struct sk_buff *skb) | |
187 | { | |
188 | struct brcmf_flowring_ring *ring; | |
189 | ||
190 | ring = flow->rings[flowid]; | |
191 | ||
192 | skb_queue_tail(&ring->skblist, skb); | |
193 | ||
194 | if (!ring->blocked && | |
195 | (skb_queue_len(&ring->skblist) > BRCMF_FLOWRING_HIGH)) { | |
196 | brcmf_txflowblock(flow->dev, true); | |
197 | brcmf_dbg(MSGBUF, "Flowcontrol: BLOCK for ring %d\n", flowid); | |
198 | ring->blocked = 1; | |
199 | } | |
200 | } | |
201 | ||
202 | ||
203 | struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid) | |
204 | { | |
205 | struct brcmf_flowring_ring *ring; | |
206 | struct sk_buff *skb; | |
207 | ||
208 | ring = flow->rings[flowid]; | |
209 | if (ring->status != RING_OPEN) | |
210 | return NULL; | |
211 | ||
212 | skb = skb_dequeue(&ring->skblist); | |
213 | ||
214 | if (ring->blocked && | |
215 | (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)) { | |
216 | brcmf_txflowblock(flow->dev, false); | |
217 | brcmf_dbg(MSGBUF, "Flowcontrol: OPEN for ring %d\n", flowid); | |
218 | ring->blocked = 0; | |
219 | } | |
220 | ||
221 | return skb; | |
222 | } | |
223 | ||
224 | ||
225 | void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, | |
226 | struct sk_buff *skb) | |
227 | { | |
228 | struct brcmf_flowring_ring *ring; | |
229 | ||
230 | ring = flow->rings[flowid]; | |
231 | ||
232 | skb_queue_head(&ring->skblist, skb); | |
233 | } | |
234 | ||
235 | ||
236 | u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid) | |
237 | { | |
238 | struct brcmf_flowring_ring *ring; | |
239 | ||
240 | ring = flow->rings[flowid]; | |
241 | if (!ring) | |
242 | return 0; | |
243 | ||
244 | if (ring->status != RING_OPEN) | |
245 | return 0; | |
246 | ||
247 | return skb_queue_len(&ring->skblist); | |
248 | } | |
249 | ||
250 | ||
251 | void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid) | |
252 | { | |
253 | struct brcmf_flowring_ring *ring; | |
254 | ||
255 | ring = flow->rings[flowid]; | |
256 | if (!ring) { | |
257 | brcmf_err("Ring NULL, for flowid %d\n", flowid); | |
258 | return; | |
259 | } | |
260 | ||
261 | ring->status = RING_OPEN; | |
262 | } | |
263 | ||
264 | ||
265 | u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid) | |
266 | { | |
267 | struct brcmf_flowring_ring *ring; | |
268 | u8 hash_idx; | |
269 | ||
270 | ring = flow->rings[flowid]; | |
271 | hash_idx = ring->hash_id; | |
272 | ||
273 | return flow->hash[hash_idx].ifidx; | |
274 | } | |
275 | ||
276 | ||
277 | struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) | |
278 | { | |
279 | struct brcmf_flowring *flow; | |
280 | u32 i; | |
281 | ||
282 | flow = kzalloc(sizeof(*flow), GFP_ATOMIC); | |
283 | if (flow) { | |
284 | flow->dev = dev; | |
285 | flow->nrofrings = nrofrings; | |
286 | for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++) | |
287 | flow->addr_mode[i] = ADDR_INDIRECT; | |
288 | for (i = 0; i < ARRAY_SIZE(flow->hash); i++) | |
289 | flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; | |
290 | flow->rings = kcalloc(nrofrings, sizeof(*flow->rings), | |
291 | GFP_ATOMIC); | |
292 | if (!flow->rings) { | |
293 | kfree(flow); | |
294 | flow = NULL; | |
295 | } | |
296 | } | |
297 | ||
298 | return flow; | |
299 | } | |
300 | ||
301 | ||
302 | void brcmf_flowring_detach(struct brcmf_flowring *flow) | |
303 | { | |
304 | struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); | |
305 | struct brcmf_pub *drvr = bus_if->drvr; | |
306 | u8 flowid; | |
307 | ||
308 | for (flowid = 0; flowid < flow->nrofrings; flowid++) { | |
309 | if (flow->rings[flowid]) | |
310 | brcmf_msgbuf_delete_flowring(drvr, flowid); | |
311 | } | |
312 | kfree(flow->rings); | |
313 | kfree(flow); | |
314 | } | |
315 | ||
316 | ||
317 | void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, | |
318 | enum proto_addr_mode addr_mode) | |
319 | { | |
320 | struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); | |
321 | struct brcmf_pub *drvr = bus_if->drvr; | |
322 | u32 i; | |
323 | u8 flowid; | |
324 | ||
325 | if (flow->addr_mode[ifidx] != addr_mode) { | |
326 | for (i = 0; i < ARRAY_SIZE(flow->hash); i++) { | |
327 | if (flow->hash[i].ifidx == ifidx) { | |
328 | flowid = flow->hash[i].flowid; | |
329 | if (flow->rings[flowid]->status != RING_OPEN) | |
330 | continue; | |
331 | flow->rings[flowid]->status = RING_CLOSING; | |
332 | brcmf_msgbuf_delete_flowring(drvr, flowid); | |
333 | } | |
334 | } | |
335 | flow->addr_mode[ifidx] = addr_mode; | |
336 | } | |
337 | } | |
338 | ||
339 | ||
340 | void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, | |
341 | u8 peer[ETH_ALEN]) | |
342 | { | |
343 | struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); | |
344 | struct brcmf_pub *drvr = bus_if->drvr; | |
345 | struct brcmf_flowring_hash *hash; | |
346 | u32 i; | |
347 | u8 flowid; | |
348 | bool sta; | |
349 | ||
350 | sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); | |
351 | hash = flow->hash; | |
352 | for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { | |
353 | if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) && | |
354 | (hash[i].ifidx == ifidx)) { | |
355 | flowid = flow->hash[i].flowid; | |
356 | if (flow->rings[flowid]->status == RING_OPEN) { | |
357 | flow->rings[flowid]->status = RING_CLOSING; | |
358 | brcmf_msgbuf_delete_flowring(drvr, flowid); | |
359 | } | |
360 | } | |
361 | } | |
362 | } |