2 * DHD Protocol Module for CDC and BDC.
4 * Copyright (C) 1999-2010, Broadcom Corporation
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:
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.
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.
24 * $Id: dhd_cdc.c,v 1.22.4.2.4.7.2.41 2010/06/23 19:58:18 Exp $
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).
36 #include <bcmendian.h>
38 #include <dngl_stats.h>
40 #include <dhd_proto.h>
44 extern int dhd_preinit_ioctls(dhd_pub_t *dhd);
46 /* Packet alignment for most efficient SDIO (can change based on platform) */
48 #define DHD_SDALIGN 32
50 #if !ISPOWEROF2(DHD_SDALIGN)
51 #error DHD_SDALIGN is not a power of 2!
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.
59 #define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for
60 * round off at the end of buffer
63 typedef struct dhd_prot {
67 uint8 bus_header[BUS_HEADER_LEN];
69 unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
73 dhdcdc_msg(dhd_pub_t *dhd)
75 dhd_prot_t *prot = dhd->prot;
76 int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
79 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
81 dhd_os_wake_lock(dhd);
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
87 if (len > CDC_MAX_MSG_SIZE)
88 len = CDC_MAX_MSG_SIZE;
91 ret = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
92 dhd_os_wake_unlock(dhd);
97 dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
100 dhd_prot_t *prot = dhd->prot;
102 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
105 ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, len+sizeof(cdc_ioctl_t));
108 } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
114 dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
116 dhd_prot_t *prot = dhd->prot;
117 cdc_ioctl_t *msg = &prot->msg;
119 int ret = 0, retries = 0;
120 uint32 id, flags = 0;
122 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
123 DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
126 /* Respond "bcmerror" and "bcmerrorstr" with local cache */
127 if (cmd == WLC_GET_VAR && buf)
129 if (!strcmp((char *)buf, "bcmerrorstr"))
131 strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
134 else if (!strcmp((char *)buf, "bcmerror"))
136 *(int *)buf = dhd->dongle_error;
141 memset(msg, 0, sizeof(cdc_ioctl_t));
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);
150 memcpy(prot->buf, buf, len);
152 if ((ret = dhdcdc_msg(dhd)) < 0) {
153 DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
158 /* wait for interrupt and get first fragment */
159 if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
162 flags = ltoh32(msg->flags);
163 id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
165 if ((id < prot->reqid) && (++retries < RETRIES))
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));
174 /* Check info buffer */
175 info = (void*)&msg[1];
177 /* Copy info buffer */
182 memcpy(buf, info, len);
185 /* Check the ERROR flag */
186 if (flags & CDCF_IOC_ERROR)
188 ret = ltoh32(msg->status);
189 /* Cache error from dongle */
190 dhd->dongle_error = ret;
198 dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
200 dhd_prot_t *prot = dhd->prot;
201 cdc_ioctl_t *msg = &prot->msg;
205 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
206 DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
208 memset(msg, 0, sizeof(cdc_ioctl_t));
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);
217 memcpy(prot->buf, buf, len);
219 if ((ret = dhdcdc_msg(dhd)) < 0)
222 if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
225 flags = ltoh32(msg->flags);
226 id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
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));
235 /* Check the ERROR flag */
236 if (flags & CDCF_IOC_ERROR)
238 ret = ltoh32(msg->status);
239 /* Cache error from dongle */
240 dhd->dongle_error = ret;
247 extern int dhd_bus_interface(struct dhd_bus *bus, uint arg, void* arg2);
249 dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
251 dhd_prot_t *prot = dhd->prot;
254 if (dhd->busstate == DHD_BUS_DOWN) {
255 DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
258 dhd_os_proto_block(dhd);
260 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
262 ASSERT(len <= WLC_IOCTL_MAXLEN);
264 if (len > WLC_IOCTL_MAXLEN)
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));
277 prot->pending = TRUE;
278 prot->lastcmd = ioc->cmd;
280 ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len);
282 ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len);
284 ioc->used = ret - sizeof(cdc_ioctl_t);
287 /* Too many programs assume ioctl() returns 0 on success */
291 cdc_ioctl_t *msg = &prot->msg;
292 ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
295 /* Intercept the wme_dp ioctl here */
296 if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
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);
305 prot->pending = FALSE;
308 dhd_os_proto_unblock(dhd);
314 dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
315 void *params, int plen, void *arg, int len, bool set)
317 return BCME_UNSUPPORTED;
321 dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
323 bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
328 dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
331 struct bdc_header *h;
334 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
337 /* Push BDC header used to convey priority for buses that don't */
340 PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
342 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
344 h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
345 if (PKTSUMNEEDED(pktbuf))
346 h->flags |= BDC_FLAG_SUM_NEEDED;
349 h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
353 BDC_SET_IF_IDX(h, ifidx);
358 dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits)
361 struct bdc_header *h;
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));
369 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
371 *fcbits = h->priority >> BDC_PRIORITY_FC_SHIFT;
372 if ((h->flags2 & BDC_FLAG2_FC_FLAG) == BDC_FLAG2_FC_FLAG)
380 dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf)
383 struct bdc_header *h;
386 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
389 /* Pop BDC header used to convey priority for buses that don't */
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));
397 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
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));
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));
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);
417 PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
419 PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
426 dhd_prot_attach(dhd_pub_t *dhd)
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__));
436 if (!(cdc = (dhd_prot_t *)dhd_os_prealloc(DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
437 DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
440 #endif /* DHD_USE_STATIC_BUF */
441 memset(cdc, 0, sizeof(dhd_prot_t));
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"));
451 dhd->hdrlen += BDC_HEADER_LEN;
453 dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
457 #ifndef DHD_USE_STATIC_BUF
459 MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
464 /* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
466 dhd_prot_detach(dhd_pub_t *dhd)
468 #ifndef DHD_USE_STATIC_BUF
469 MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
475 dhd_prot_dstats(dhd_pub_t *dhd)
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;
488 dhd_prot_init(dhd_pub_t *dhd)
493 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
495 dhd_os_proto_block(dhd);
497 /* Get the device MAC address */
498 strcpy(buf, "cur_etheraddr");
499 ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
501 dhd_os_proto_unblock(dhd);
504 memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
506 dhd_os_proto_unblock(dhd);
508 #ifdef EMBEDDED_PLATFORM
509 ret = dhd_preinit_ioctls(dhd);
510 #endif /* EMBEDDED_PLATFORM */
512 /* Always assumes wl for now */
519 dhd_prot_stop(dhd_pub_t *dhd)
521 /* Nothing to do for CDC */