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