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 } 523