OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / hardware / broadcom / wlan / bcm4329 / src / dhd / sys / 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 extern 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
63 typedef 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
72 static int
73 dhdcdc_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
96 static int
97 dhdcdc_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
113 int
114 dhdcdc_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                 DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
154                 goto done;
155         }
156
157 retry:
158         /* wait for interrupt and get first fragment */
159         if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
160                 goto done;
161
162         flags = ltoh32(msg->flags);
163         id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
164
165         if ((id < prot->reqid) && (++retries < RETRIES))
166                 goto retry;
167         if (id != prot->reqid) {
168                 DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
169                            dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
170                 ret = -EINVAL;
171                 goto done;
172         }
173
174         /* Check info buffer */
175         info = (void*)&msg[1];
176
177         /* Copy info buffer */
178         if (buf)
179         {
180                 if (ret < (int)len)
181                         len = ret;
182                 memcpy(buf, info, len);
183         }
184
185         /* Check the ERROR flag */
186         if (flags & CDCF_IOC_ERROR)
187         {
188                 ret = ltoh32(msg->status);
189                 /* Cache error from dongle */
190                 dhd->dongle_error = ret;
191         }
192
193 done:
194         return ret;
195 }
196
197 int
198 dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
199 {
200         dhd_prot_t *prot = dhd->prot;
201         cdc_ioctl_t *msg = &prot->msg;
202         int ret = 0;
203         uint32 flags, id;
204
205         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
206         DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
207
208         memset(msg, 0, sizeof(cdc_ioctl_t));
209
210         msg->cmd = htol32(cmd);
211         msg->len = htol32(len);
212         msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
213         CDC_SET_IF_IDX(msg, ifidx);
214         msg->flags = htol32(msg->flags);
215
216         if (buf)
217                 memcpy(prot->buf, buf, len);
218
219         if ((ret = dhdcdc_msg(dhd)) < 0)
220                 goto done;
221
222         if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
223                 goto done;
224
225         flags = ltoh32(msg->flags);
226         id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
227
228         if (id != prot->reqid) {
229                 DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
230                            dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
231                 ret = -EINVAL;
232                 goto done;
233         }
234
235         /* Check the ERROR flag */
236         if (flags & CDCF_IOC_ERROR)
237         {
238                 ret = ltoh32(msg->status);
239                 /* Cache error from dongle */
240                 dhd->dongle_error = ret;
241         }
242
243 done:
244         return ret;
245 }
246
247 extern int dhd_bus_interface(struct dhd_bus *bus, uint arg, void* arg2);
248 int
249 dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
250 {
251         dhd_prot_t *prot = dhd->prot;
252         int ret = -1;
253
254         if (dhd->busstate == DHD_BUS_DOWN) {
255                 DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
256                 return ret;
257         }
258         dhd_os_proto_block(dhd);
259
260         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
261
262         ASSERT(len <= WLC_IOCTL_MAXLEN);
263
264         if (len > WLC_IOCTL_MAXLEN)
265                 goto done;
266
267         if (prot->pending == TRUE) {
268                 DHD_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
269                         ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
270                         (unsigned long)prot->lastcmd));
271                 if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
272                         DHD_TRACE(("iovar cmd=%s\n", (char*)buf));
273                 }
274                 goto done;
275         }
276
277         prot->pending = TRUE;
278         prot->lastcmd = ioc->cmd;
279         if (ioc->set)
280                 ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len);
281         else {
282                 ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len);
283                 if (ret > 0)
284                         ioc->used = ret - sizeof(cdc_ioctl_t);
285         }
286
287         /* Too many programs assume ioctl() returns 0 on success */
288         if (ret >= 0)
289                 ret = 0;
290         else {
291                 cdc_ioctl_t *msg = &prot->msg;
292                 ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
293         }
294
295         /* Intercept the wme_dp ioctl here */
296         if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
297                 int slen, val = 0;
298
299                 slen = strlen("wme_dp") + 1;
300                 if (len >= (int)(slen + sizeof(int)))
301                         bcopy(((char *)buf + slen), &val, sizeof(int));
302                 dhd->wme_dp = (uint8) ltoh32(val);
303         }
304
305         prot->pending = FALSE;
306
307 done:
308         dhd_os_proto_unblock(dhd);
309
310         return ret;
311 }
312
313 int
314 dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
315                   void *params, int plen, void *arg, int len, bool set)
316 {
317         return BCME_UNSUPPORTED;
318 }
319
320 void
321 dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
322 {
323         bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
324 }
325
326
327 void
328 dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
329 {
330 #ifdef BDC
331         struct bdc_header *h;
332 #endif /* BDC */
333
334         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
335
336 #ifdef BDC
337         /* Push BDC header used to convey priority for buses that don't */
338
339
340         PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
341
342         h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
343
344         h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
345         if (PKTSUMNEEDED(pktbuf))
346                 h->flags |= BDC_FLAG_SUM_NEEDED;
347
348
349         h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
350         h->flags2 = 0;
351         h->rssi = 0;
352 #endif /* BDC */
353         BDC_SET_IF_IDX(h, ifidx);
354 }
355
356
357 bool
358 dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits)
359 {
360 #ifdef BDC
361         struct bdc_header *h;
362
363         if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
364                 DHD_ERROR(("%s: rx data too short (%d < %d)\n",
365                         __FUNCTION__, PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
366                 return BCME_ERROR;
367         }
368
369         h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
370
371         *fcbits = h->priority >> BDC_PRIORITY_FC_SHIFT;
372         if ((h->flags2 & BDC_FLAG2_FC_FLAG) == BDC_FLAG2_FC_FLAG)
373                 return TRUE;
374 #endif
375         return FALSE;
376 }
377
378
379 int
380 dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf)
381 {
382 #ifdef BDC
383         struct bdc_header *h;
384 #endif
385
386         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
387
388 #ifdef BDC
389         /* Pop BDC header used to convey priority for buses that don't */
390
391         if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
392                 DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
393                            PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
394                 return BCME_ERROR;
395         }
396
397         h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
398
399         if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
400                 DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
401                            __FUNCTION__, *ifidx));
402                 return BCME_ERROR;
403         }
404
405         if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
406                 DHD_ERROR(("%s: non-BDC packet received, flags 0x%x\n",
407                            dhd_ifname(dhd, *ifidx), h->flags));
408                 return BCME_ERROR;
409         }
410
411         if (h->flags & BDC_FLAG_SUM_GOOD) {
412                 DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
413                           dhd_ifname(dhd, *ifidx), h->flags));
414                 PKTSETSUMGOOD(pktbuf, TRUE);
415         }
416
417         PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
418
419         PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
420 #endif /* BDC */
421
422         return 0;
423 }
424
425 int
426 dhd_prot_attach(dhd_pub_t *dhd)
427 {
428         dhd_prot_t *cdc;
429
430 #ifndef DHD_USE_STATIC_BUF
431         if (!(cdc = (dhd_prot_t *)MALLOC(dhd->osh, sizeof(dhd_prot_t)))) {
432                 DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
433                 goto fail;
434         }
435 #else
436         if (!(cdc = (dhd_prot_t *)dhd_os_prealloc(DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
437                 DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
438                 goto fail;
439         }
440 #endif /* DHD_USE_STATIC_BUF */
441         memset(cdc, 0, sizeof(dhd_prot_t));
442
443         /* ensure that the msg buf directly follows the cdc msg struct */
444         if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
445                 DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
446                 goto fail;
447         }
448
449         dhd->prot = cdc;
450 #ifdef BDC
451         dhd->hdrlen += BDC_HEADER_LEN;
452 #endif
453         dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
454         return 0;
455
456 fail:
457 #ifndef DHD_USE_STATIC_BUF
458         if (cdc != NULL)
459                 MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
460 #endif
461         return BCME_NOMEM;
462 }
463
464 /* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
465 void
466 dhd_prot_detach(dhd_pub_t *dhd)
467 {
468 #ifndef DHD_USE_STATIC_BUF
469         MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
470 #endif
471         dhd->prot = NULL;
472 }
473
474 void
475 dhd_prot_dstats(dhd_pub_t *dhd)
476 {
477         /* No stats from dongle added yet, copy bus stats */
478         dhd->dstats.tx_packets = dhd->tx_packets;
479         dhd->dstats.tx_errors = dhd->tx_errors;
480         dhd->dstats.rx_packets = dhd->rx_packets;
481         dhd->dstats.rx_errors = dhd->rx_errors;
482         dhd->dstats.rx_dropped = dhd->rx_dropped;
483         dhd->dstats.multicast = dhd->rx_multicast;
484         return;
485 }
486
487 int
488 dhd_prot_init(dhd_pub_t *dhd)
489 {
490         int ret = 0;
491         char buf[128];
492
493         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
494
495         dhd_os_proto_block(dhd);
496
497         /* Get the device MAC address */
498         strcpy(buf, "cur_etheraddr");
499         ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
500         if (ret < 0) {
501                 dhd_os_proto_unblock(dhd);
502                 return ret;
503         }
504         memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
505
506         dhd_os_proto_unblock(dhd);
507
508 #ifdef EMBEDDED_PLATFORM
509         ret = dhd_preinit_ioctls(dhd);
510 #endif /* EMBEDDED_PLATFORM */
511
512         /* Always assumes wl for now */
513         dhd->iswl = TRUE;
514
515         return ret;
516 }
517
518 void
519 dhd_prot_stop(dhd_pub_t *dhd)
520 {
521         /* Nothing to do for CDC */
522 }