aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/bcm4329/dhd_cdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/bcm4329/dhd_cdc.c')
-rw-r--r--drivers/net/wireless/bcm4329/dhd_cdc.c535
1 files changed, 535 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcm4329/dhd_cdc.c b/drivers/net/wireless/bcm4329/dhd_cdc.c
new file mode 100644
index 00000000000..4bec0b606dc
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_cdc.c
@@ -0,0 +1,535 @@
1/*
2 * DHD Protocol Module for CDC and BDC.
3 *
4 * Copyright (C) 1999-2010, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: dhd_cdc.c,v 1.22.4.2.4.7.2.41 2010/06/23 19:58:18 Exp $
25 *
26 * BDC is like CDC, except it includes a header for data packets to convey
27 * packet priority over the bus, and flags (e.g. to indicate checksum status
28 * for dongle offload).
29 */
30
31#include <typedefs.h>
32#include <osl.h>
33
34#include <bcmutils.h>
35#include <bcmcdc.h>
36#include <bcmendian.h>
37
38#include <dngl_stats.h>
39#include <dhd.h>
40#include <dhd_proto.h>
41#include <dhd_bus.h>
42#include <dhd_dbg.h>
43
44extern int dhd_preinit_ioctls(dhd_pub_t *dhd);
45
46/* Packet alignment for most efficient SDIO (can change based on platform) */
47#ifndef DHD_SDALIGN
48#define DHD_SDALIGN 32
49#endif
50#if !ISPOWEROF2(DHD_SDALIGN)
51#error DHD_SDALIGN is not a power of 2!
52#endif
53
54#define RETRIES 2 /* # of retries to retrieve matching ioctl response */
55#define BUS_HEADER_LEN (16+DHD_SDALIGN) /* Must be atleast SDPCM_RESERVE
56 * defined in dhd_sdio.c (amount of header tha might be added)
57 * plus any space that might be needed for alignment padding.
58 */
59#define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for
60 * round off at the end of buffer
61 */
62
63typedef struct dhd_prot {
64 uint16 reqid;
65 uint8 pending;
66 uint32 lastcmd;
67 uint8 bus_header[BUS_HEADER_LEN];
68 cdc_ioctl_t msg;
69 unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
70} dhd_prot_t;
71
72static int
73dhdcdc_msg(dhd_pub_t *dhd)
74{
75 dhd_prot_t *prot = dhd->prot;
76 int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
77 int ret;
78
79 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
80
81 dhd_os_wake_lock(dhd);
82
83 /* NOTE : cdc->msg.len holds the desired length of the buffer to be
84 * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
85 * is actually sent to the dongle
86 */
87 if (len > CDC_MAX_MSG_SIZE)
88 len = CDC_MAX_MSG_SIZE;
89
90 /* Send request */
91 ret = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
92 dhd_os_wake_unlock(dhd);
93 return ret;
94}
95
96static int
97dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
98{
99 int ret;
100 dhd_prot_t *prot = dhd->prot;
101
102 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
103
104 do {
105 ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, len+sizeof(cdc_ioctl_t));
106 if (ret < 0)
107 break;
108 } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
109
110 return ret;
111}
112
113int
114dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
115{
116 dhd_prot_t *prot = dhd->prot;
117 cdc_ioctl_t *msg = &prot->msg;
118 void *info;
119 int ret = 0, retries = 0;
120 uint32 id, flags = 0;
121
122 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
123 DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
124
125
126 /* Respond "bcmerror" and "bcmerrorstr" with local cache */
127 if (cmd == WLC_GET_VAR && buf)
128 {
129 if (!strcmp((char *)buf, "bcmerrorstr"))
130 {
131 strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
132 goto done;
133 }
134 else if (!strcmp((char *)buf, "bcmerror"))
135 {
136 *(int *)buf = dhd->dongle_error;
137 goto done;
138 }
139 }
140
141 memset(msg, 0, sizeof(cdc_ioctl_t));
142
143 msg->cmd = htol32(cmd);
144 msg->len = htol32(len);
145 msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
146 CDC_SET_IF_IDX(msg, ifidx);
147 msg->flags = htol32(msg->flags);
148
149 if (buf)
150 memcpy(prot->buf, buf, len);
151
152 if ((ret = dhdcdc_msg(dhd)) < 0) {
153 if (!dhd->hang_was_sent)
154 DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
155 goto done;
156 }
157
158retry:
159 /* wait for interrupt and get first fragment */
160 if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
161 goto done;
162
163 flags = ltoh32(msg->flags);
164 id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
165
166 if ((id < prot->reqid) && (++retries < RETRIES))
167 goto retry;
168 if (id != prot->reqid) {
169 DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
170 dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
171 ret = -EINVAL;
172 goto done;
173 }
174
175 /* Check info buffer */
176 info = (void*)&msg[1];
177
178 /* Copy info buffer */
179 if (buf)
180 {
181 if (ret < (int)len)
182 len = ret;
183 memcpy(buf, info, len);
184 }
185
186 /* Check the ERROR flag */
187 if (flags & CDCF_IOC_ERROR)
188 {
189 ret = ltoh32(msg->status);
190 /* Cache error from dongle */
191 dhd->dongle_error = ret;
192 }
193
194done:
195 return ret;
196}
197
198int
199dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
200{
201 dhd_prot_t *prot = dhd->prot;
202 cdc_ioctl_t *msg = &prot->msg;
203 int ret = 0;
204 uint32 flags, id;
205
206 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
207 DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
208
209 if (dhd->busstate == DHD_BUS_DOWN) {
210 DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
211 return -EIO;
212 }
213
214 /* don't talk to the dongle if fw is about to be reloaded */
215 if (dhd->hang_was_sent) {
216 DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n",
217 __FUNCTION__));
218 return -EIO;
219 }
220
221 memset(msg, 0, sizeof(cdc_ioctl_t));
222
223 msg->cmd = htol32(cmd);
224 msg->len = htol32(len);
225 msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
226 CDC_SET_IF_IDX(msg, ifidx);
227 msg->flags = htol32(msg->flags);
228
229 if (buf)
230 memcpy(prot->buf, buf, len);
231
232 if ((ret = dhdcdc_msg(dhd)) < 0)
233 goto done;
234
235 if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
236 goto done;
237
238 flags = ltoh32(msg->flags);
239 id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
240
241 if (id != prot->reqid) {
242 DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
243 dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
244 ret = -EINVAL;
245 goto done;
246 }
247
248 /* Check the ERROR flag */
249 if (flags & CDCF_IOC_ERROR)
250 {
251 ret = ltoh32(msg->status);
252 /* Cache error from dongle */
253 dhd->dongle_error = ret;
254 }
255
256done:
257 return ret;
258}
259
260extern int dhd_bus_interface(struct dhd_bus *bus, uint arg, void* arg2);
261int
262dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
263{
264 dhd_prot_t *prot = dhd->prot;
265 int ret = -1;
266
267 if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) {
268 DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
269 return ret;
270 }
271 dhd_os_proto_block(dhd);
272
273 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
274
275 ASSERT(len <= WLC_IOCTL_MAXLEN);
276
277 if (len > WLC_IOCTL_MAXLEN)
278 goto done;
279
280 if (prot->pending == TRUE) {
281 DHD_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
282 ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
283 (unsigned long)prot->lastcmd));
284 if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
285 DHD_TRACE(("iovar cmd=%s\n", (char*)buf));
286 }
287 goto done;
288 }
289
290 prot->pending = TRUE;
291 prot->lastcmd = ioc->cmd;
292 if (ioc->set)
293 ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len);
294 else {
295 ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len);
296 if (ret > 0)
297 ioc->used = ret - sizeof(cdc_ioctl_t);
298 }
299
300 /* Too many programs assume ioctl() returns 0 on success */
301 if (ret >= 0)
302 ret = 0;
303 else {
304 cdc_ioctl_t *msg = &prot->msg;
305 ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
306 }
307
308 /* Intercept the wme_dp ioctl here */
309 if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
310 int slen, val = 0;
311
312 slen = strlen("wme_dp") + 1;
313 if (len >= (int)(slen + sizeof(int)))
314 bcopy(((char *)buf + slen), &val, sizeof(int));
315 dhd->wme_dp = (uint8) ltoh32(val);
316 }
317
318 prot->pending = FALSE;
319
320done:
321 dhd_os_proto_unblock(dhd);
322
323 return ret;
324}
325
326int
327dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
328 void *params, int plen, void *arg, int len, bool set)
329{
330 return BCME_UNSUPPORTED;
331}
332
333void
334dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
335{
336 bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
337}
338
339
340void
341dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
342{
343#ifdef BDC
344 struct bdc_header *h;
345#endif /* BDC */
346
347 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
348
349#ifdef BDC
350 /* Push BDC header used to convey priority for buses that don't */
351
352
353 PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
354
355 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
356
357 h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
358 if (PKTSUMNEEDED(pktbuf))
359 h->flags |= BDC_FLAG_SUM_NEEDED;
360
361
362 h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
363 h->flags2 = 0;
364 h->rssi = 0;
365#endif /* BDC */
366 BDC_SET_IF_IDX(h, ifidx);
367}
368
369
370bool
371dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits)
372{
373#ifdef BDC
374 struct bdc_header *h;
375
376 if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
377 DHD_ERROR(("%s: rx data too short (%d < %d)\n",
378 __FUNCTION__, PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
379 return BCME_ERROR;
380 }
381
382 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
383
384 *fcbits = h->priority >> BDC_PRIORITY_FC_SHIFT;
385 if ((h->flags2 & BDC_FLAG2_FC_FLAG) == BDC_FLAG2_FC_FLAG)
386 return TRUE;
387#endif
388 return FALSE;
389}
390
391
392int
393dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf)
394{
395#ifdef BDC
396 struct bdc_header *h;
397#endif
398
399 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
400
401#ifdef BDC
402 /* Pop BDC header used to convey priority for buses that don't */
403
404 if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
405 DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
406 PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
407 return BCME_ERROR;
408 }
409
410 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
411
412 if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
413 DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
414 __FUNCTION__, *ifidx));
415 return BCME_ERROR;
416 }
417
418 if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
419 DHD_ERROR(("%s: non-BDC packet received, flags 0x%x\n",
420 dhd_ifname(dhd, *ifidx), h->flags));
421 return BCME_ERROR;
422 }
423
424 if (h->flags & BDC_FLAG_SUM_GOOD) {
425 DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
426 dhd_ifname(dhd, *ifidx), h->flags));
427 PKTSETSUMGOOD(pktbuf, TRUE);
428 }
429
430 PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
431
432 PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
433#endif /* BDC */
434
435 return 0;
436}
437
438int
439dhd_prot_attach(dhd_pub_t *dhd)
440{
441 dhd_prot_t *cdc;
442
443#ifndef DHD_USE_STATIC_BUF
444 if (!(cdc = (dhd_prot_t *)MALLOC(dhd->osh, sizeof(dhd_prot_t)))) {
445 DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
446 goto fail;
447 }
448#else
449 if (!(cdc = (dhd_prot_t *)dhd_os_prealloc(DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
450 DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
451 goto fail;
452 }
453#endif /* DHD_USE_STATIC_BUF */
454 memset(cdc, 0, sizeof(dhd_prot_t));
455
456 /* ensure that the msg buf directly follows the cdc msg struct */
457 if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
458 DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
459 goto fail;
460 }
461
462 dhd->prot = cdc;
463#ifdef BDC
464 dhd->hdrlen += BDC_HEADER_LEN;
465#endif
466 dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
467 return 0;
468
469fail:
470#ifndef DHD_USE_STATIC_BUF
471 if (cdc != NULL)
472 MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
473#endif
474 return BCME_NOMEM;
475}
476
477/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
478void
479dhd_prot_detach(dhd_pub_t *dhd)
480{
481#ifndef DHD_USE_STATIC_BUF
482 MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
483#endif
484 dhd->prot = NULL;
485}
486
487void
488dhd_prot_dstats(dhd_pub_t *dhd)
489{
490 /* No stats from dongle added yet, copy bus stats */
491 dhd->dstats.tx_packets = dhd->tx_packets;
492 dhd->dstats.tx_errors = dhd->tx_errors;
493 dhd->dstats.rx_packets = dhd->rx_packets;
494 dhd->dstats.rx_errors = dhd->rx_errors;
495 dhd->dstats.rx_dropped = dhd->rx_dropped;
496 dhd->dstats.multicast = dhd->rx_multicast;
497 return;
498}
499
500int
501dhd_prot_init(dhd_pub_t *dhd)
502{
503 int ret = 0;
504 char buf[128];
505
506 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
507
508 dhd_os_proto_block(dhd);
509
510 /* Get the device MAC address */
511 strcpy(buf, "cur_etheraddr");
512 ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
513 if (ret < 0) {
514 dhd_os_proto_unblock(dhd);
515 return ret;
516 }
517 memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
518
519 dhd_os_proto_unblock(dhd);
520
521#ifdef EMBEDDED_PLATFORM
522 ret = dhd_preinit_ioctls(dhd);
523#endif /* EMBEDDED_PLATFORM */
524
525 /* Always assumes wl for now */
526 dhd->iswl = TRUE;
527
528 return ret;
529}
530
531void
532dhd_prot_stop(dhd_pub_t *dhd)
533{
534 /* Nothing to do for CDC */
535}