1
2
3
4
5
6
7
8
9
10
11#include <linux/types.h>
12#include <linux/netdevice.h>
13
14#include <brcmu_utils.h>
15#include <brcmu_wifi.h>
16
17#include "core.h"
18#include "bus.h"
19#include "fwsignal.h"
20#include "debug.h"
21#include "tracepoint.h"
22#include "proto.h"
23#include "bcdc.h"
24
25struct brcmf_proto_bcdc_dcmd {
26 __le32 cmd;
27 __le32 len;
28
29 __le32 flags;
30 __le32 status;
31};
32
33
34#define BCDC_DCMD_ERROR 0x01
35#define BCDC_DCMD_SET 0x02
36#define BCDC_DCMD_IF_MASK 0xF000
37#define BCDC_DCMD_IF_SHIFT 12
38#define BCDC_DCMD_ID_MASK 0xFFFF0000
39#define BCDC_DCMD_ID_SHIFT 16
40#define BCDC_DCMD_ID(flags) \
41 (((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT)
42
43
44
45
46
47#define BCDC_HEADER_LEN 4
48#define BCDC_PROTO_VER 2
49#define BCDC_FLAG_VER_MASK 0xf0
50#define BCDC_FLAG_VER_SHIFT 4
51#define BCDC_FLAG_SUM_GOOD 0x04
52#define BCDC_FLAG_SUM_NEEDED 0x08
53#define BCDC_PRIORITY_MASK 0x7
54#define BCDC_FLAG2_IF_MASK 0x0f
55#define BCDC_FLAG2_IF_SHIFT 0
56
57#define BCDC_GET_IF_IDX(hdr) \
58 ((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT))
59#define BCDC_SET_IF_IDX(hdr, idx) \
60 ((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \
61 ((idx) << BCDC_FLAG2_IF_SHIFT)))
62
63
64
65
66
67
68
69
70
71struct brcmf_proto_bcdc_header {
72 u8 flags;
73 u8 priority;
74 u8 flags2;
75 u8 data_offset;
76};
77
78
79
80
81
82#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 12
83
84#define RETRIES 2
85#define BUS_HEADER_LEN (16+64)
86
87
88
89
90struct brcmf_bcdc {
91 u16 reqid;
92 u8 bus_header[BUS_HEADER_LEN];
93 struct brcmf_proto_bcdc_dcmd msg;
94 unsigned char buf[BRCMF_DCMD_MAXLEN];
95 struct brcmf_fws_info *fws;
96};
97
98
99struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr)
100{
101 struct brcmf_bcdc *bcdc = drvr->proto->pd;
102
103 return bcdc->fws;
104}
105
106static int
107brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
108 uint len, bool set)
109{
110 struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
111 struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
112 u32 flags;
113
114 brcmf_dbg(BCDC, "Enter\n");
115
116 memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
117
118 msg->cmd = cpu_to_le32(cmd);
119 msg->len = cpu_to_le32(len);
120 flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);
121 if (set)
122 flags |= BCDC_DCMD_SET;
123 flags = (flags & ~BCDC_DCMD_IF_MASK) |
124 (ifidx << BCDC_DCMD_IF_SHIFT);
125 msg->flags = cpu_to_le32(flags);
126
127 if (buf)
128 memcpy(bcdc->buf, buf, len);
129
130 len += sizeof(*msg);
131 if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE)
132 len = BRCMF_TX_IOCTL_MAX_MSG_SIZE;
133
134
135 return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len);
136}
137
138static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
139{
140 int ret;
141 struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
142
143 brcmf_dbg(BCDC, "Enter\n");
144 len += sizeof(struct brcmf_proto_bcdc_dcmd);
145 do {
146 ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg,
147 len);
148 if (ret < 0)
149 break;
150 } while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id);
151
152 return ret;
153}
154
155static int
156brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
157 void *buf, uint len, int *fwerr)
158{
159 struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
160 struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
161 void *info;
162 int ret = 0, retries = 0;
163 u32 id, flags;
164
165 brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
166
167 *fwerr = 0;
168 ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);
169 if (ret < 0) {
170 bphy_err(drvr, "brcmf_proto_bcdc_msg failed w/status %d\n",
171 ret);
172 goto done;
173 }
174
175retry:
176
177 ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
178 if (ret < 0)
179 goto done;
180
181 flags = le32_to_cpu(msg->flags);
182 id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
183
184 if ((id < bcdc->reqid) && (++retries < RETRIES))
185 goto retry;
186 if (id != bcdc->reqid) {
187 bphy_err(drvr, "%s: unexpected request id %d (expected %d)\n",
188 brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
189 bcdc->reqid);
190 ret = -EINVAL;
191 goto done;
192 }
193
194
195 info = (void *)&bcdc->buf[0];
196
197
198 if (buf) {
199 if (ret < (int)len)
200 len = ret;
201 memcpy(buf, info, len);
202 }
203
204 ret = 0;
205
206
207 if (flags & BCDC_DCMD_ERROR)
208 *fwerr = le32_to_cpu(msg->status);
209done:
210 return ret;
211}
212
213static int
214brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
215 void *buf, uint len, int *fwerr)
216{
217 struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
218 struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
219 int ret;
220 u32 flags, id;
221
222 brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
223
224 *fwerr = 0;
225 ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);
226 if (ret < 0)
227 goto done;
228
229 ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
230 if (ret < 0)
231 goto done;
232
233 flags = le32_to_cpu(msg->flags);
234 id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
235
236 if (id != bcdc->reqid) {
237 bphy_err(drvr, "%s: unexpected request id %d (expected %d)\n",
238 brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
239 bcdc->reqid);
240 ret = -EINVAL;
241 goto done;
242 }
243
244 ret = 0;
245
246
247 if (flags & BCDC_DCMD_ERROR)
248 *fwerr = le32_to_cpu(msg->status);
249
250done:
251 return ret;
252}
253
254static void
255brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
256 struct sk_buff *pktbuf)
257{
258 struct brcmf_proto_bcdc_header *h;
259
260 brcmf_dbg(BCDC, "Enter\n");
261
262
263 skb_push(pktbuf, BCDC_HEADER_LEN);
264
265 h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
266
267 h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT);
268 if (pktbuf->ip_summed == CHECKSUM_PARTIAL)
269 h->flags |= BCDC_FLAG_SUM_NEEDED;
270
271 h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK);
272 h->flags2 = 0;
273 h->data_offset = offset;
274 BCDC_SET_IF_IDX(h, ifidx);
275 trace_brcmf_bcdchdr(pktbuf->data);
276}
277
278static int
279brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
280 struct sk_buff *pktbuf, struct brcmf_if **ifp)
281{
282 struct brcmf_proto_bcdc_header *h;
283 struct brcmf_if *tmp_if;
284
285 brcmf_dbg(BCDC, "Enter\n");
286
287
288 if (pktbuf->len <= BCDC_HEADER_LEN) {
289 brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",
290 pktbuf->len, BCDC_HEADER_LEN);
291 return -EBADE;
292 }
293
294 trace_brcmf_bcdchdr(pktbuf->data);
295 h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
296
297 tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
298 if (!tmp_if) {
299 brcmf_dbg(INFO, "no matching ifp found\n");
300 return -EBADE;
301 }
302 if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
303 BCDC_PROTO_VER) {
304 bphy_err(drvr, "%s: non-BCDC packet received, flags 0x%x\n",
305 brcmf_ifname(tmp_if), h->flags);
306 return -EBADE;
307 }
308
309 if (h->flags & BCDC_FLAG_SUM_GOOD) {
310 brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
311 brcmf_ifname(tmp_if), h->flags);
312 pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
313 }
314
315 pktbuf->priority = h->priority & BCDC_PRIORITY_MASK;
316
317 skb_pull(pktbuf, BCDC_HEADER_LEN);
318 if (do_fws)
319 brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
320 else
321 skb_pull(pktbuf, h->data_offset << 2);
322
323 if (pktbuf->len == 0)
324 return -ENODATA;
325
326 if (ifp != NULL)
327 *ifp = tmp_if;
328 return 0;
329}
330
331static int brcmf_proto_bcdc_tx_queue_data(struct brcmf_pub *drvr, int ifidx,
332 struct sk_buff *skb)
333{
334 struct brcmf_if *ifp = brcmf_get_ifp(drvr, ifidx);
335 struct brcmf_bcdc *bcdc = drvr->proto->pd;
336
337 if (!brcmf_fws_queue_skbs(bcdc->fws))
338 return brcmf_proto_txdata(drvr, ifidx, 0, skb);
339
340 return brcmf_fws_process_skb(ifp, skb);
341}
342
343static int
344brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset,
345 struct sk_buff *pktbuf)
346{
347 brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf);
348 return brcmf_bus_txdata(drvr->bus_if, pktbuf);
349}
350
351void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state)
352{
353 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
354 struct brcmf_pub *drvr = bus_if->drvr;
355
356 brcmf_dbg(TRACE, "Enter\n");
357
358 brcmf_fws_bus_blocked(drvr, state);
359}
360
361void
362brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp,
363 bool success)
364{
365 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
366 struct brcmf_bcdc *bcdc = bus_if->drvr->proto->pd;
367 struct brcmf_if *ifp;
368
369
370 if (brcmf_fws_fc_active(bcdc->fws)) {
371 if (!success)
372 brcmf_fws_bustxfail(bcdc->fws, txp);
373 } else {
374 if (brcmf_proto_bcdc_hdrpull(bus_if->drvr, false, txp, &ifp))
375 brcmu_pkt_buf_free_skb(txp);
376 else
377 brcmf_txfinalize(ifp, txp, success);
378 }
379}
380
381static void
382brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
383 enum proto_addr_mode addr_mode)
384{
385}
386
387static void
388brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx,
389 u8 peer[ETH_ALEN])
390{
391}
392
393static void
394brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx,
395 u8 peer[ETH_ALEN])
396{
397}
398
399static void brcmf_proto_bcdc_rxreorder(struct brcmf_if *ifp,
400 struct sk_buff *skb)
401{
402 brcmf_fws_rxreorder(ifp, skb);
403}
404
405static void
406brcmf_proto_bcdc_add_if(struct brcmf_if *ifp)
407{
408 brcmf_fws_add_interface(ifp);
409}
410
411static void
412brcmf_proto_bcdc_del_if(struct brcmf_if *ifp)
413{
414 brcmf_fws_del_interface(ifp);
415}
416
417static void
418brcmf_proto_bcdc_reset_if(struct brcmf_if *ifp)
419{
420 brcmf_fws_reset_interface(ifp);
421}
422
423static int
424brcmf_proto_bcdc_init_done(struct brcmf_pub *drvr)
425{
426 struct brcmf_bcdc *bcdc = drvr->proto->pd;
427 struct brcmf_fws_info *fws;
428
429 fws = brcmf_fws_attach(drvr);
430 if (IS_ERR(fws))
431 return PTR_ERR(fws);
432
433 bcdc->fws = fws;
434 return 0;
435}
436
437static void brcmf_proto_bcdc_debugfs_create(struct brcmf_pub *drvr)
438{
439 brcmf_fws_debugfs_create(drvr);
440}
441
442int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
443{
444 struct brcmf_bcdc *bcdc;
445
446 bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC);
447 if (!bcdc)
448 goto fail;
449
450
451 if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) {
452 bphy_err(drvr, "struct brcmf_proto_bcdc is not correctly defined\n");
453 goto fail;
454 }
455
456 drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull;
457 drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd;
458 drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd;
459 drvr->proto->tx_queue_data = brcmf_proto_bcdc_tx_queue_data;
460 drvr->proto->txdata = brcmf_proto_bcdc_txdata;
461 drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
462 drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
463 drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
464 drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder;
465 drvr->proto->add_if = brcmf_proto_bcdc_add_if;
466 drvr->proto->del_if = brcmf_proto_bcdc_del_if;
467 drvr->proto->reset_if = brcmf_proto_bcdc_reset_if;
468 drvr->proto->init_done = brcmf_proto_bcdc_init_done;
469 drvr->proto->debugfs_create = brcmf_proto_bcdc_debugfs_create;
470 drvr->proto->pd = bcdc;
471
472 drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
473 drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
474 sizeof(struct brcmf_proto_bcdc_dcmd);
475 return 0;
476
477fail:
478 kfree(bcdc);
479 return -ENOMEM;
480}
481
482void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)
483{
484 struct brcmf_bcdc *bcdc = drvr->proto->pd;
485
486 drvr->proto->pd = NULL;
487 brcmf_fws_detach(bcdc->fws);
488 kfree(bcdc);
489}
490