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 uint8 wlan_mac_addr[ETHER_ADDR_LEN];
46 extern int dhd_preinit_ioctls(dhd_pub_t *dhd);
48 /* Packet alignment for most efficient SDIO (can change based on platform) */
50 #define DHD_SDALIGN 32
52 #if !ISPOWEROF2(DHD_SDALIGN)
53 #error DHD_SDALIGN is not a power of 2!
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.
61 #define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for
62 * round off at the end of buffer
65 typedef struct dhd_prot {
69 uint8 bus_header[BUS_HEADER_LEN];
71 unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
75 dhdcdc_msg(dhd_pub_t *dhd)
77 dhd_prot_t *prot = dhd->prot;
78 int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
81 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
83 dhd_os_wake_lock(dhd);
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
89 if (len > CDC_MAX_MSG_SIZE)
90 len = CDC_MAX_MSG_SIZE;
93 ret = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
94 dhd_os_wake_unlock(dhd);
99 dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
102 dhd_prot_t *prot = dhd->prot;
104 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
107 ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, len+sizeof(cdc_ioctl_t));
110 } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
116 dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
118 dhd_prot_t *prot = dhd->prot;
119 cdc_ioctl_t *msg = &prot->msg;
121 int ret = 0, retries = 0;
122 uint32 id, flags = 0;
124 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
125 DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
128 /* Respond "bcmerror" and "bcmerrorstr" with local cache */
129 if (cmd == WLC_GET_VAR && buf)
131 if (!strcmp((char *)buf, "bcmerrorstr"))
133 strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
136 else if (!strcmp((char *)buf, "bcmerror"))
138 *(int *)buf = dhd->dongle_error;
143 memset(msg, 0, sizeof(cdc_ioctl_t));
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);
152 memcpy(prot->buf, buf, len);
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));
161 /* wait for interrupt and get first fragment */
162 if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
165 flags = ltoh32(msg->flags);
166 id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
168 if ((id < prot->reqid) && (++retries < RETRIES))
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));
177 /* Check info buffer */
178 info = (void*)&msg[1];
180 /* Copy info buffer */
185 memcpy(buf, info, len);
188 /* Check the ERROR flag */
189 if (flags & CDCF_IOC_ERROR)
191 ret = ltoh32(msg->status);
192 /* Cache error from dongle */
193 dhd->dongle_error = ret;
201 dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
203 dhd_prot_t *prot = dhd->prot;
204 cdc_ioctl_t *msg = &prot->msg;
208 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
209 DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
211 if (dhd->busstate == DHD_BUS_DOWN) {
212 DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
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",
223 memset(msg, 0, sizeof(cdc_ioctl_t));
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);
232 memcpy(prot->buf, buf, len);
234 if ((ret = dhdcdc_msg(dhd)) < 0)
237 if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
240 flags = ltoh32(msg->flags);
241 id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
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));
250 /* Check the ERROR flag */
251 if (flags & CDCF_IOC_ERROR)
253 ret = ltoh32(msg->status);
254 /* Cache error from dongle */
255 dhd->dongle_error = ret;
262 extern int dhd_bus_interface(struct dhd_bus *bus, uint arg, void* arg2);
264 dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
266 dhd_prot_t *prot = dhd->prot;
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__));
273 dhd_os_proto_block(dhd);
275 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
277 ASSERT(len <= WLC_IOCTL_MAXLEN);
279 if (len > WLC_IOCTL_MAXLEN)
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));
292 prot->pending = TRUE;
293 prot->lastcmd = ioc->cmd;
295 ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len);
297 ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len);
299 ioc->used = ret - sizeof(cdc_ioctl_t);
302 /* Too many programs assume ioctl() returns 0 on success */
306 cdc_ioctl_t *msg = &prot->msg;
307 ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
310 /* Intercept the wme_dp ioctl here */
311 if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
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);
320 prot->pending = FALSE;
323 dhd_os_proto_unblock(dhd);
329 dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
330 void *params, int plen, void *arg, int len, bool set)
332 return BCME_UNSUPPORTED;
336 dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
338 bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
343 dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
346 struct bdc_header *h;
349 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
352 /* Push BDC header used to convey priority for buses that don't */
355 PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
357 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
359 h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
360 if (PKTSUMNEEDED(pktbuf))
361 h->flags |= BDC_FLAG_SUM_NEEDED;
364 h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
368 BDC_SET_IF_IDX(h, ifidx);
373 dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits)
376 struct bdc_header *h;
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));
384 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
386 *fcbits = h->priority >> BDC_PRIORITY_FC_SHIFT;
387 if ((h->flags2 & BDC_FLAG2_FC_FLAG) == BDC_FLAG2_FC_FLAG)
395 dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf)
398 struct bdc_header *h;
401 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
404 /* Pop BDC header used to convey priority for buses that don't */
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));
412 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
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));
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));
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);
432 PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
434 PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
441 dhd_prot_attach(dhd_pub_t *dhd)
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__));
451 if (!(cdc = (dhd_prot_t *)dhd_os_prealloc(DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
452 DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
455 #endif /* DHD_USE_STATIC_BUF */
456 memset(cdc, 0, sizeof(dhd_prot_t));
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"));
466 dhd->hdrlen += BDC_HEADER_LEN;
468 dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
472 #ifndef DHD_USE_STATIC_BUF
474 MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
479 /* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
481 dhd_prot_detach(dhd_pub_t *dhd)
483 #ifndef DHD_USE_STATIC_BUF
484 MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
490 dhd_prot_dstats(dhd_pub_t *dhd)
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;
503 dhd_prot_init(dhd_pub_t *dhd)
508 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
510 dhd_os_proto_block(dhd);
512 /* Get the device MAC address */
513 strcpy(buf, "cur_etheraddr");
514 ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
516 dhd_os_proto_unblock(dhd);
519 memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
520 memcpy(wlan_mac_addr, buf, ETHER_ADDR_LEN);
522 dhd_os_proto_unblock(dhd);
524 #ifdef EMBEDDED_PLATFORM
525 ret = dhd_preinit_ioctls(dhd);
526 #endif /* EMBEDDED_PLATFORM */
528 /* Always assumes wl for now */
535 dhd_prot_stop(dhd_pub_t *dhd)
537 /* Nothing to do for CDC */