/drivers/staging/brcm80211/brcmfmac/dhd_cdc.c
C | 502 lines | 356 code | 94 blank | 52 comment | 53 complexity | 076a9c808c873bbbcc8b8500aab90a0d MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
1/* 2 * Copyright (c) 2010 Broadcom Corporation 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17#include <linux/types.h> 18#include <linux/netdevice.h> 19#include <linux/sched.h> 20#include <defs.h> 21 22#include <brcmu_utils.h> 23#include <brcmu_wifi.h> 24 25#include "dhd.h" 26#include "dhd_proto.h" 27#include "dhd_bus.h" 28#include "dhd_dbg.h" 29 30struct brcmf_proto_cdc_ioctl { 31 u32 cmd; /* ioctl command value */ 32 u32 len; /* lower 16: output buflen; 33 * upper 16: input buflen (excludes header) */ 34 u32 flags; /* flag defns given below */ 35 u32 status; /* status code returned from the device */ 36}; 37 38/* Max valid buffer size that can be sent to the dongle */ 39#define CDC_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) 40 41/* CDC flag definitions */ 42#define CDCF_IOC_ERROR 0x01 /* 1=ioctl cmd failed */ 43#define CDCF_IOC_SET 0x02 /* 0=get, 1=set cmd */ 44#define CDCF_IOC_IF_MASK 0xF000 /* I/F index */ 45#define CDCF_IOC_IF_SHIFT 12 46#define CDCF_IOC_ID_MASK 0xFFFF0000 /* id an ioctl pairing */ 47#define CDCF_IOC_ID_SHIFT 16 /* ID Mask shift bits */ 48#define CDC_IOC_ID(flags) \ 49 (((flags) & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT) 50#define CDC_SET_IF_IDX(hdr, idx) \ 51 ((hdr)->flags = (((hdr)->flags & ~CDCF_IOC_IF_MASK) | \ 52 ((idx) << CDCF_IOC_IF_SHIFT))) 53 54/* 55 * BDC header - Broadcom specific extension of CDC. 56 * Used on data packets to convey priority across USB. 57 */ 58#define BDC_HEADER_LEN 4 59#define BDC_PROTO_VER 1 /* Protocol version */ 60#define BDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */ 61#define BDC_FLAG_VER_SHIFT 4 /* Protocol version shift */ 62#define BDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */ 63#define BDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */ 64#define BDC_PRIORITY_MASK 0x7 65#define BDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */ 66#define BDC_FLAG2_IF_SHIFT 0 67 68#define BDC_GET_IF_IDX(hdr) \ 69 ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT)) 70#define BDC_SET_IF_IDX(hdr, idx) \ 71 ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \ 72 ((idx) << BDC_FLAG2_IF_SHIFT))) 73 74struct brcmf_proto_bdc_header { 75 u8 flags; 76 u8 priority; /* 802.1d Priority, 4:7 flow control info for usb */ 77 u8 flags2; 78 u8 rssi; 79}; 80 81 82#define RETRIES 2 /* # of retries to retrieve matching ioctl response */ 83#define BUS_HEADER_LEN (16+BRCMF_SDALIGN) /* Must be atleast SDPCM_RESERVE 84 * (amount of header tha might be added) 85 * plus any space that might be needed 86 * for alignment padding. 87 */ 88#define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for 89 * round off at the end of buffer 90 */ 91 92struct brcmf_proto { 93 u16 reqid; 94 u8 pending; 95 u32 lastcmd; 96 u8 bus_header[BUS_HEADER_LEN]; 97 struct brcmf_proto_cdc_ioctl msg; 98 unsigned char buf[BRCMF_C_IOCTL_MAXLEN + ROUND_UP_MARGIN]; 99}; 100 101static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr) 102{ 103 struct brcmf_proto *prot = drvr->prot; 104 int len = le32_to_cpu(prot->msg.len) + 105 sizeof(struct brcmf_proto_cdc_ioctl); 106 107 BRCMF_TRACE(("%s: Enter\n", __func__)); 108 109 /* NOTE : cdc->msg.len holds the desired length of the buffer to be 110 * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area 111 * is actually sent to the dongle 112 */ 113 if (len > CDC_MAX_MSG_SIZE) 114 len = CDC_MAX_MSG_SIZE; 115 116 /* Send request */ 117 return brcmf_sdbrcm_bus_txctl(drvr->bus, (unsigned char *)&prot->msg, 118 len); 119} 120 121static int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) 122{ 123 int ret; 124 struct brcmf_proto *prot = drvr->prot; 125 126 BRCMF_TRACE(("%s: Enter\n", __func__)); 127 128 do { 129 ret = brcmf_sdbrcm_bus_rxctl(drvr->bus, 130 (unsigned char *)&prot->msg, 131 len + sizeof(struct brcmf_proto_cdc_ioctl)); 132 if (ret < 0) 133 break; 134 } while (CDC_IOC_ID(le32_to_cpu(prot->msg.flags)) != id); 135 136 return ret; 137} 138 139int 140brcmf_proto_cdc_query_ioctl(struct brcmf_pub *drvr, int ifidx, uint cmd, 141 void *buf, uint len) 142{ 143 struct brcmf_proto *prot = drvr->prot; 144 struct brcmf_proto_cdc_ioctl *msg = &prot->msg; 145 void *info; 146 int ret = 0, retries = 0; 147 u32 id, flags = 0; 148 149 BRCMF_TRACE(("%s: Enter\n", __func__)); 150 BRCMF_CTL(("%s: cmd %d len %d\n", __func__, cmd, len)); 151 152 /* Respond "bcmerror" and "bcmerrorstr" with local cache */ 153 if (cmd == BRCMF_C_GET_VAR && buf) { 154 if (!strcmp((char *)buf, "bcmerrorstr")) { 155 strncpy((char *)buf, "bcm_error", 156 BCME_STRLEN); 157 goto done; 158 } else if (!strcmp((char *)buf, "bcmerror")) { 159 *(int *)buf = drvr->dongle_error; 160 goto done; 161 } 162 } 163 164 memset(msg, 0, sizeof(struct brcmf_proto_cdc_ioctl)); 165 166 msg->cmd = cpu_to_le32(cmd); 167 msg->len = cpu_to_le32(len); 168 msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT); 169 CDC_SET_IF_IDX(msg, ifidx); 170 msg->flags = cpu_to_le32(msg->flags); 171 172 if (buf) 173 memcpy(prot->buf, buf, len); 174 175 ret = brcmf_proto_cdc_msg(drvr); 176 if (ret < 0) { 177 BRCMF_ERROR(("brcmf_proto_cdc_query_ioctl: brcmf_proto_cdc_msg " 178 "failed w/status %d\n", ret)); 179 goto done; 180 } 181 182retry: 183 /* wait for interrupt and get first fragment */ 184 ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); 185 if (ret < 0) 186 goto done; 187 188 flags = le32_to_cpu(msg->flags); 189 id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT; 190 191 if ((id < prot->reqid) && (++retries < RETRIES)) 192 goto retry; 193 if (id != prot->reqid) { 194 BRCMF_ERROR(("%s: %s: unexpected request id %d (expected %d)\n", 195 brcmf_ifname(drvr, ifidx), __func__, id, 196 prot->reqid)); 197 ret = -EINVAL; 198 goto done; 199 } 200 201 /* Check info buffer */ 202 info = (void *)&msg[1]; 203 204 /* Copy info buffer */ 205 if (buf) { 206 if (ret < (int)len) 207 len = ret; 208 memcpy(buf, info, len); 209 } 210 211 /* Check the ERROR flag */ 212 if (flags & CDCF_IOC_ERROR) { 213 ret = le32_to_cpu(msg->status); 214 /* Cache error from dongle */ 215 drvr->dongle_error = ret; 216 } 217 218done: 219 return ret; 220} 221 222int brcmf_proto_cdc_set_ioctl(struct brcmf_pub *drvr, int ifidx, uint cmd, 223 void *buf, uint len) 224{ 225 struct brcmf_proto *prot = drvr->prot; 226 struct brcmf_proto_cdc_ioctl *msg = &prot->msg; 227 int ret = 0; 228 u32 flags, id; 229 230 BRCMF_TRACE(("%s: Enter\n", __func__)); 231 BRCMF_CTL(("%s: cmd %d len %d\n", __func__, cmd, len)); 232 233 memset(msg, 0, sizeof(struct brcmf_proto_cdc_ioctl)); 234 235 msg->cmd = cpu_to_le32(cmd); 236 msg->len = cpu_to_le32(len); 237 msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET; 238 CDC_SET_IF_IDX(msg, ifidx); 239 msg->flags = cpu_to_le32(msg->flags); 240 241 if (buf) 242 memcpy(prot->buf, buf, len); 243 244 ret = brcmf_proto_cdc_msg(drvr); 245 if (ret < 0) 246 goto done; 247 248 ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); 249 if (ret < 0) 250 goto done; 251 252 flags = le32_to_cpu(msg->flags); 253 id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT; 254 255 if (id != prot->reqid) { 256 BRCMF_ERROR(("%s: %s: unexpected request id %d (expected %d)\n", 257 brcmf_ifname(drvr, ifidx), __func__, id, 258 prot->reqid)); 259 ret = -EINVAL; 260 goto done; 261 } 262 263 /* Check the ERROR flag */ 264 if (flags & CDCF_IOC_ERROR) { 265 ret = le32_to_cpu(msg->status); 266 /* Cache error from dongle */ 267 drvr->dongle_error = ret; 268 } 269 270done: 271 return ret; 272} 273 274int 275brcmf_proto_ioctl(struct brcmf_pub *drvr, int ifidx, struct brcmf_ioctl *ioc, 276 void *buf, int len) 277{ 278 struct brcmf_proto *prot = drvr->prot; 279 int ret = -1; 280 281 if (drvr->busstate == BRCMF_BUS_DOWN) { 282 BRCMF_ERROR(("%s : bus is down. we have nothing to do\n", 283 __func__)); 284 return ret; 285 } 286 brcmf_os_proto_block(drvr); 287 288 BRCMF_TRACE(("%s: Enter\n", __func__)); 289 290 if (len > BRCMF_C_IOCTL_MAXLEN) 291 goto done; 292 293 if (prot->pending == true) { 294 BRCMF_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) " 295 "lastcmd=0x%x (%lu)\n", 296 ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd, 297 (unsigned long)prot->lastcmd)); 298 if ((ioc->cmd == BRCMF_C_SET_VAR) || 299 (ioc->cmd == BRCMF_C_GET_VAR)) 300 BRCMF_TRACE(("iovar cmd=%s\n", (char *)buf)); 301 302 goto done; 303 } 304 305 prot->pending = true; 306 prot->lastcmd = ioc->cmd; 307 if (ioc->set) 308 ret = brcmf_proto_cdc_set_ioctl(drvr, ifidx, ioc->cmd, 309 buf, len); 310 else { 311 ret = brcmf_proto_cdc_query_ioctl(drvr, ifidx, ioc->cmd, 312 buf, len); 313 if (ret > 0) 314 ioc->used = ret - sizeof(struct brcmf_proto_cdc_ioctl); 315 } 316 317 /* Too many programs assume ioctl() returns 0 on success */ 318 if (ret >= 0) 319 ret = 0; 320 else { 321 struct brcmf_proto_cdc_ioctl *msg = &prot->msg; 322 /* len == needed when set/query fails from dongle */ 323 ioc->needed = le32_to_cpu(msg->len); 324 } 325 326 /* Intercept the wme_dp ioctl here */ 327 if (!ret && ioc->cmd == BRCMF_C_SET_VAR && 328 !strcmp(buf, "wme_dp")) { 329 int slen, val = 0; 330 331 slen = strlen("wme_dp") + 1; 332 if (len >= (int)(slen + sizeof(int))) 333 memcpy(&val, (char *)buf + slen, sizeof(int)); 334 drvr->wme_dp = (u8) le32_to_cpu(val); 335 } 336 337 prot->pending = false; 338 339done: 340 brcmf_os_proto_unblock(drvr); 341 342 return ret; 343} 344 345#define PKTSUMNEEDED(skb) \ 346 (((struct sk_buff *)(skb))->ip_summed == CHECKSUM_PARTIAL) 347#define PKTSETSUMGOOD(skb, x) \ 348 (((struct sk_buff *)(skb))->ip_summed = \ 349 ((x) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE)) 350 351void brcmf_proto_dump(struct brcmf_pub *drvr, struct brcmu_strbuf *strbuf) 352{ 353 brcmu_bprintf(strbuf, "Protocol CDC: reqid %d\n", drvr->prot->reqid); 354} 355 356void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, 357 struct sk_buff *pktbuf) 358{ 359 struct brcmf_proto_bdc_header *h; 360 361 BRCMF_TRACE(("%s: Enter\n", __func__)); 362 363 /* Push BDC header used to convey priority for buses that don't */ 364 365 skb_push(pktbuf, BDC_HEADER_LEN); 366 367 h = (struct brcmf_proto_bdc_header *)(pktbuf->data); 368 369 h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); 370 if (PKTSUMNEEDED(pktbuf)) 371 h->flags |= BDC_FLAG_SUM_NEEDED; 372 373 h->priority = (pktbuf->priority & BDC_PRIORITY_MASK); 374 h->flags2 = 0; 375 h->rssi = 0; 376 BDC_SET_IF_IDX(h, ifidx); 377} 378 379int brcmf_proto_hdrpull(struct brcmf_pub *drvr, int *ifidx, 380 struct sk_buff *pktbuf) 381{ 382 struct brcmf_proto_bdc_header *h; 383 384 BRCMF_TRACE(("%s: Enter\n", __func__)); 385 386 /* Pop BDC header used to convey priority for buses that don't */ 387 388 if (pktbuf->len < BDC_HEADER_LEN) { 389 BRCMF_ERROR(("%s: rx data too short (%d < %d)\n", __func__, 390 pktbuf->len, BDC_HEADER_LEN)); 391 return -EBADE; 392 } 393 394 h = (struct brcmf_proto_bdc_header *)(pktbuf->data); 395 396 *ifidx = BDC_GET_IF_IDX(h); 397 if (*ifidx >= BRCMF_MAX_IFS) { 398 BRCMF_ERROR(("%s: rx data ifnum out of range (%d)\n", 399 __func__, *ifidx)); 400 return -EBADE; 401 } 402 403 if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != 404 BDC_PROTO_VER) { 405 BRCMF_ERROR(("%s: non-BDC packet received, flags 0x%x\n", 406 brcmf_ifname(drvr, *ifidx), h->flags)); 407 return -EBADE; 408 } 409 410 if (h->flags & BDC_FLAG_SUM_GOOD) { 411 BRCMF_INFO(("%s: BDC packet received with good rx-csum, " 412 "flags 0x%x\n", 413 brcmf_ifname(drvr, *ifidx), h->flags)); 414 PKTSETSUMGOOD(pktbuf, true); 415 } 416 417 pktbuf->priority = h->priority & BDC_PRIORITY_MASK; 418 419 skb_pull(pktbuf, BDC_HEADER_LEN); 420 421 return 0; 422} 423 424int brcmf_proto_attach(struct brcmf_pub *drvr) 425{ 426 struct brcmf_proto *cdc; 427 428 cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC); 429 if (!cdc) { 430 BRCMF_ERROR(("%s: kmalloc failed\n", __func__)); 431 goto fail; 432 } 433 434 /* ensure that the msg buf directly follows the cdc msg struct */ 435 if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) { 436 BRCMF_ERROR(("struct brcmf_proto is not correctly defined\n")); 437 goto fail; 438 } 439 440 drvr->prot = cdc; 441 drvr->hdrlen += BDC_HEADER_LEN; 442 drvr->maxctl = BRCMF_C_IOCTL_MAXLEN + 443 sizeof(struct brcmf_proto_cdc_ioctl) + ROUND_UP_MARGIN; 444 return 0; 445 446fail: 447 kfree(cdc); 448 return -ENOMEM; 449} 450 451/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */ 452void brcmf_proto_detach(struct brcmf_pub *drvr) 453{ 454 kfree(drvr->prot); 455 drvr->prot = NULL; 456} 457 458void brcmf_proto_dstats(struct brcmf_pub *drvr) 459{ 460 /* No stats from dongle added yet, copy bus stats */ 461 drvr->dstats.tx_packets = drvr->tx_packets; 462 drvr->dstats.tx_errors = drvr->tx_errors; 463 drvr->dstats.rx_packets = drvr->rx_packets; 464 drvr->dstats.rx_errors = drvr->rx_errors; 465 drvr->dstats.rx_dropped = drvr->rx_dropped; 466 drvr->dstats.multicast = drvr->rx_multicast; 467 return; 468} 469 470int brcmf_proto_init(struct brcmf_pub *drvr) 471{ 472 int ret = 0; 473 char buf[128]; 474 475 BRCMF_TRACE(("%s: Enter\n", __func__)); 476 477 brcmf_os_proto_block(drvr); 478 479 /* Get the device MAC address */ 480 strcpy(buf, "cur_etheraddr"); 481 ret = brcmf_proto_cdc_query_ioctl(drvr, 0, BRCMF_C_GET_VAR, 482 buf, sizeof(buf)); 483 if (ret < 0) { 484 brcmf_os_proto_unblock(drvr); 485 return ret; 486 } 487 memcpy(drvr->mac, buf, ETH_ALEN); 488 489 brcmf_os_proto_unblock(drvr); 490 491 ret = brcmf_c_preinit_ioctls(drvr); 492 493 /* Always assumes wl for now */ 494 drvr->iswl = true; 495 496 return ret; 497} 498 499void brcmf_proto_stop(struct brcmf_pub *drvr) 500{ 501 /* Nothing to do for CDC */ 502}