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/******************************************************************************* 18 * Communicates with the dongle by using dcmd codes. 19 * For certain dcmd codes, the dongle interprets string data from the host. 20 ******************************************************************************/ 21 22#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 23 24#include <linux/types.h> 25#include <linux/netdevice.h> 26#include <linux/sched.h> 27#include <defs.h> 28 29#include <brcmu_utils.h> 30#include <brcmu_wifi.h> 31 32#include "dhd.h" 33#include "dhd_proto.h" 34#include "dhd_bus.h" 35#include "dhd_dbg.h" 36 37struct brcmf_proto_cdc_dcmd { 38 __le32 cmd; /* dongle command value */ 39 __le32 len; /* lower 16: output buflen; 40 * upper 16: input buflen (excludes header) */ 41 __le32 flags; /* flag defns given below */ 42 __le32 status; /* status code returned from the device */ 43}; 44 45/* Max valid buffer size that can be sent to the dongle */ 46#define CDC_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) 47 48/* CDC flag definitions */ 49#define CDC_DCMD_ERROR 0x01 /* 1=cmd failed */ 50#define CDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */ 51#define CDC_DCMD_IF_MASK 0xF000 /* I/F index */ 52#define CDC_DCMD_IF_SHIFT 12 53#define CDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */ 54#define CDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */ 55#define CDC_DCMD_ID(flags) \ 56 (((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT) 57 58/* 59 * BDC header - Broadcom specific extension of CDC. 60 * Used on data packets to convey priority across USB. 61 */ 62#define BDC_HEADER_LEN 4 63#define BDC_PROTO_VER 2 /* Protocol version */ 64#define BDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */ 65#define BDC_FLAG_VER_SHIFT 4 /* Protocol version shift */ 66#define BDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */ 67#define BDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */ 68#define BDC_PRIORITY_MASK 0x7 69#define BDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */ 70#define BDC_FLAG2_IF_SHIFT 0 71 72#define BDC_GET_IF_IDX(hdr) \ 73 ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT)) 74#define BDC_SET_IF_IDX(hdr, idx) \ 75 ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \ 76 ((idx) << BDC_FLAG2_IF_SHIFT))) 77 78struct brcmf_proto_bdc_header { 79 u8 flags; 80 u8 priority; /* 802.1d Priority, 4:7 flow control info for usb */ 81 u8 flags2; 82 u8 data_offset; 83}; 84 85 86#define RETRIES 2 /* # of retries to retrieve matching dcmd response */ 87#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE 88 * (amount of header tha might be added) 89 * plus any space that might be needed 90 * for bus alignment padding. 91 */ 92#define ROUND_UP_MARGIN 2048 /* Biggest bus block size possible for 93 * round off at the end of buffer 94 * Currently is SDIO 95 */ 96 97struct brcmf_proto { 98 u16 reqid; 99 u8 pending; 100 u32 lastcmd; 101 u8 bus_header[BUS_HEADER_LEN]; 102 struct brcmf_proto_cdc_dcmd msg; 103 unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN]; 104}; 105 106static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr) 107{ 108 struct brcmf_proto *prot = drvr->prot; 109 int len = le32_to_cpu(prot->msg.len) + 110 sizeof(struct brcmf_proto_cdc_dcmd); 111 112 brcmf_dbg(TRACE, "Enter\n"); 113 114 /* NOTE : cdc->msg.len holds the desired length of the buffer to be 115 * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area 116 * is actually sent to the dongle 117 */ 118 if (len > CDC_MAX_MSG_SIZE) 119 len = CDC_MAX_MSG_SIZE; 120 121 /* Send request */ 122 return drvr->bus_if->brcmf_bus_txctl(drvr->dev, 123 (unsigned char *)&prot->msg, 124 len); 125} 126 127static int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) 128{ 129 int ret; 130 struct brcmf_proto *prot = drvr->prot; 131 132 brcmf_dbg(TRACE, "Enter\n"); 133 134 do { 135 ret = drvr->bus_if->brcmf_bus_rxctl(drvr->dev, 136 (unsigned char *)&prot->msg, 137 len + sizeof(struct brcmf_proto_cdc_dcmd)); 138 if (ret < 0) 139 break; 140 } while (CDC_DCMD_ID(le32_to_cpu(prot->msg.flags)) != id); 141 142 return ret; 143} 144 145int 146brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, 147 void *buf, uint len) 148{ 149 struct brcmf_proto *prot = drvr->prot; 150 struct brcmf_proto_cdc_dcmd *msg = &prot->msg; 151 void *info; 152 int ret = 0, retries = 0; 153 u32 id, flags; 154 155 brcmf_dbg(TRACE, "Enter\n"); 156 brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len); 157 158 /* Respond "bcmerror" and "bcmerrorstr" with local cache */ 159 if (cmd == BRCMF_C_GET_VAR && buf) { 160 if (!strcmp((char *)buf, "bcmerrorstr")) { 161 strncpy((char *)buf, "bcm_error", 162 BCME_STRLEN); 163 goto done; 164 } else if (!strcmp((char *)buf, "bcmerror")) { 165 *(int *)buf = drvr->dongle_error; 166 goto done; 167 } 168 } 169 170 memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); 171 172 msg->cmd = cpu_to_le32(cmd); 173 msg->len = cpu_to_le32(len); 174 flags = (++prot->reqid << CDC_DCMD_ID_SHIFT); 175 flags = (flags & ~CDC_DCMD_IF_MASK) | 176 (ifidx << CDC_DCMD_IF_SHIFT); 177 msg->flags = cpu_to_le32(flags); 178 179 if (buf) 180 memcpy(prot->buf, buf, len); 181 182 ret = brcmf_proto_cdc_msg(drvr); 183 if (ret < 0) { 184 brcmf_dbg(ERROR, "brcmf_proto_cdc_msg failed w/status %d\n", 185 ret); 186 goto done; 187 } 188 189retry: 190 /* wait for interrupt and get first fragment */ 191 ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); 192 if (ret < 0) 193 goto done; 194 195 flags = le32_to_cpu(msg->flags); 196 id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT; 197 198 if ((id < prot->reqid) && (++retries < RETRIES)) 199 goto retry; 200 if (id != prot->reqid) { 201 brcmf_dbg(ERROR, "%s: unexpected request id %d (expected %d)\n", 202 brcmf_ifname(drvr, ifidx), id, prot->reqid); 203 ret = -EINVAL; 204 goto done; 205 } 206 207 /* Check info buffer */ 208 info = (void *)&msg[1]; 209 210 /* Copy info buffer */ 211 if (buf) { 212 if (ret < (int)len) 213 len = ret; 214 memcpy(buf, info, len); 215 } 216 217 /* Check the ERROR flag */ 218 if (flags & CDC_DCMD_ERROR) { 219 ret = le32_to_cpu(msg->status); 220 /* Cache error from dongle */ 221 drvr->dongle_error = ret; 222 } 223 224done: 225 return ret; 226} 227 228int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, 229 void *buf, uint len) 230{ 231 struct brcmf_proto *prot = drvr->prot; 232 struct brcmf_proto_cdc_dcmd *msg = &prot->msg; 233 int ret = 0; 234 u32 flags, id; 235 236 brcmf_dbg(TRACE, "Enter\n"); 237 brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len); 238 239 memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); 240 241 msg->cmd = cpu_to_le32(cmd); 242 msg->len = cpu_to_le32(len); 243 flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET; 244 flags = (flags & ~CDC_DCMD_IF_MASK) | 245 (ifidx << CDC_DCMD_IF_SHIFT); 246 msg->flags = cpu_to_le32(flags); 247 248 if (buf) 249 memcpy(prot->buf, buf, len); 250 251 ret = brcmf_proto_cdc_msg(drvr); 252 if (ret < 0) 253 goto done; 254 255 ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); 256 if (ret < 0) 257 goto done; 258 259 flags = le32_to_cpu(msg->flags); 260 id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT; 261 262 if (id != prot->reqid) { 263 brcmf_dbg(ERROR, "%s: unexpected request id %d (expected %d)\n", 264 brcmf_ifname(drvr, ifidx), id, prot->reqid); 265 ret = -EINVAL; 266 goto done; 267 } 268 269 /* Check the ERROR flag */ 270 if (flags & CDC_DCMD_ERROR) { 271 ret = le32_to_cpu(msg->status); 272 /* Cache error from dongle */ 273 drvr->dongle_error = ret; 274 } 275 276done: 277 return ret; 278} 279 280int 281brcmf_proto_dcmd(struct brcmf_pub *drvr, int ifidx, struct brcmf_dcmd *dcmd, 282 int len) 283{ 284 struct brcmf_proto *prot = drvr->prot; 285 int ret = -1; 286 287 if (drvr->bus_if->state == BRCMF_BUS_DOWN) { 288 brcmf_dbg(ERROR, "bus is down. we have nothing to do.\n"); 289 return ret; 290 } 291 mutex_lock(&drvr->proto_block); 292 293 brcmf_dbg(TRACE, "Enter\n"); 294 295 if (len > BRCMF_DCMD_MAXLEN) 296 goto done; 297 298 if (prot->pending == true) { 299 brcmf_dbg(TRACE, "CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n", 300 dcmd->cmd, (unsigned long)dcmd->cmd, prot->lastcmd, 301 (unsigned long)prot->lastcmd); 302 if (dcmd->cmd == BRCMF_C_SET_VAR || 303 dcmd->cmd == BRCMF_C_GET_VAR) 304 brcmf_dbg(TRACE, "iovar cmd=%s\n", (char *)dcmd->buf); 305 306 goto done; 307 } 308 309 prot->pending = true; 310 prot->lastcmd = dcmd->cmd; 311 if (dcmd->set) 312 ret = brcmf_proto_cdc_set_dcmd(drvr, ifidx, dcmd->cmd, 313 dcmd->buf, len); 314 else { 315 ret = brcmf_proto_cdc_query_dcmd(drvr, ifidx, dcmd->cmd, 316 dcmd->buf, len); 317 if (ret > 0) 318 dcmd->used = ret - 319 sizeof(struct brcmf_proto_cdc_dcmd); 320 } 321 322 if (ret >= 0) 323 ret = 0; 324 else { 325 struct brcmf_proto_cdc_dcmd *msg = &prot->msg; 326 /* len == needed when set/query fails from dongle */ 327 dcmd->needed = le32_to_cpu(msg->len); 328 } 329 330 /* Intercept the wme_dp dongle cmd here */ 331 if (!ret && dcmd->cmd == BRCMF_C_SET_VAR && 332 !strcmp(dcmd->buf, "wme_dp")) { 333 int slen; 334 __le32 val = 0; 335 336 slen = strlen("wme_dp") + 1; 337 if (len >= (int)(slen + sizeof(int))) 338 memcpy(&val, (char *)dcmd->buf + slen, sizeof(int)); 339 drvr->wme_dp = (u8) le32_to_cpu(val); 340 } 341 342 prot->pending = false; 343 344done: 345 mutex_unlock(&drvr->proto_block); 346 347 return ret; 348} 349 350static bool pkt_sum_needed(struct sk_buff *skb) 351{ 352 return skb->ip_summed == CHECKSUM_PARTIAL; 353} 354 355static void pkt_set_sum_good(struct sk_buff *skb, bool x) 356{ 357 skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE); 358} 359 360void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, 361 struct sk_buff *pktbuf) 362{ 363 struct brcmf_proto_bdc_header *h; 364 365 brcmf_dbg(TRACE, "Enter\n"); 366 367 /* Push BDC header used to convey priority for buses that don't */ 368 369 skb_push(pktbuf, BDC_HEADER_LEN); 370 371 h = (struct brcmf_proto_bdc_header *)(pktbuf->data); 372 373 h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); 374 if (pkt_sum_needed(pktbuf)) 375 h->flags |= BDC_FLAG_SUM_NEEDED; 376 377 h->priority = (pktbuf->priority & BDC_PRIORITY_MASK); 378 h->flags2 = 0; 379 h->data_offset = 0; 380 BDC_SET_IF_IDX(h, ifidx); 381} 382 383int brcmf_proto_hdrpull(struct device *dev, int *ifidx, 384 struct sk_buff *pktbuf) 385{ 386 struct brcmf_proto_bdc_header *h; 387 struct brcmf_bus *bus_if = dev_get_drvdata(dev); 388 struct brcmf_pub *drvr = bus_if->drvr; 389 390 brcmf_dbg(TRACE, "Enter\n"); 391 392 /* Pop BDC header used to convey priority for buses that don't */ 393 394 if (pktbuf->len < BDC_HEADER_LEN) { 395 brcmf_dbg(ERROR, "rx data too short (%d < %d)\n", 396 pktbuf->len, BDC_HEADER_LEN); 397 return -EBADE; 398 } 399 400 h = (struct brcmf_proto_bdc_header *)(pktbuf->data); 401 402 *ifidx = BDC_GET_IF_IDX(h); 403 if (*ifidx >= BRCMF_MAX_IFS) { 404 brcmf_dbg(ERROR, "rx data ifnum out of range (%d)\n", *ifidx); 405 return -EBADE; 406 } 407 408 if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != 409 BDC_PROTO_VER) { 410 brcmf_dbg(ERROR, "%s: non-BDC packet received, flags 0x%x\n", 411 brcmf_ifname(drvr, *ifidx), h->flags); 412 return -EBADE; 413 } 414 415 if (h->flags & BDC_FLAG_SUM_GOOD) { 416 brcmf_dbg(INFO, "%s: BDC packet received with good rx-csum, flags 0x%x\n", 417 brcmf_ifname(drvr, *ifidx), h->flags); 418 pkt_set_sum_good(pktbuf, true); 419 } 420 421 pktbuf->priority = h->priority & BDC_PRIORITY_MASK; 422 423 skb_pull(pktbuf, BDC_HEADER_LEN); 424 425 return 0; 426} 427 428int brcmf_proto_attach(struct brcmf_pub *drvr) 429{ 430 struct brcmf_proto *cdc; 431 432 cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC); 433 if (!cdc) 434 goto fail; 435 436 /* ensure that the msg buf directly follows the cdc msg struct */ 437 if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) { 438 brcmf_dbg(ERROR, "struct brcmf_proto is not correctly defined\n"); 439 goto fail; 440 } 441 442 drvr->prot = cdc; 443 drvr->hdrlen += BDC_HEADER_LEN; 444 drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + 445 sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN; 446 return 0; 447 448fail: 449 kfree(cdc); 450 return -ENOMEM; 451} 452 453/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */ 454void brcmf_proto_detach(struct brcmf_pub *drvr) 455{ 456 kfree(drvr->prot); 457 drvr->prot = NULL; 458} 459 460int brcmf_proto_init(struct brcmf_pub *drvr) 461{ 462 int ret = 0; 463 char buf[128]; 464 465 brcmf_dbg(TRACE, "Enter\n"); 466 467 mutex_lock(&drvr->proto_block); 468 469 /* Get the device MAC address */ 470 strcpy(buf, "cur_etheraddr"); 471 ret = brcmf_proto_cdc_query_dcmd(drvr, 0, BRCMF_C_GET_VAR, 472 buf, sizeof(buf)); 473 if (ret < 0) { 474 mutex_unlock(&drvr->proto_block); 475 return ret; 476 } 477 memcpy(drvr->mac, buf, ETH_ALEN); 478 479 mutex_unlock(&drvr->proto_block); 480 481 ret = brcmf_c_preinit_dcmds(drvr); 482 483 /* Always assumes wl for now */ 484 drvr->iswl = true; 485 486 return ret; 487} 488 489void brcmf_proto_stop(struct brcmf_pub *drvr) 490{ 491 /* Nothing to do for CDC */ 492} 493