fwsignal.c revision 43fa635e16c0674c9248b7feb271084c2874bb0a
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#include <linux/types.h> 17#include <linux/if_ether.h> 18#include <linux/spinlock.h> 19#include <linux/skbuff.h> 20#include <linux/netdevice.h> 21#include <linux/err.h> 22#include <uapi/linux/nl80211.h> 23 24#include <brcmu_utils.h> 25#include <brcmu_wifi.h> 26#include "dhd.h" 27#include "dhd_dbg.h" 28#include "fwil.h" 29#include "fweh.h" 30#include "fwsignal.h" 31 32/** 33 * DOC: Firmware Signalling 34 * 35 * Firmware can send signals to host and vice versa, which are passed in the 36 * data packets using TLV based header. This signalling layer is on top of the 37 * BDC bus protocol layer. 38 */ 39 40/* 41 * single definition for firmware-driver flow control tlv's. 42 * 43 * each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length). 44 * A length value 0 indicates variable length tlv. 45 */ 46#define BRCMF_FWS_TLV_DEFLIST \ 47 BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \ 48 BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \ 49 BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \ 50 BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \ 51 BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \ 52 BRCMF_FWS_TLV_DEF(MACDESC_ADD, 6, 8) \ 53 BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \ 54 BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \ 55 BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \ 56 BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \ 57 BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 8) \ 58 BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \ 59 BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \ 60 BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \ 61 BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \ 62 BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \ 63 BRCMF_FWS_TLV_DEF(FILLER, 255, 0) 64 65/** 66 * enum brcmf_fws_tlv_type - definition of tlv identifiers. 67 */ 68#define BRCMF_FWS_TLV_DEF(name, id, len) \ 69 BRCMF_FWS_TYPE_ ## name = id, 70enum brcmf_fws_tlv_type { 71 BRCMF_FWS_TLV_DEFLIST 72 BRCMF_FWS_TYPE_INVALID 73}; 74#undef BRCMF_FWS_TLV_DEF 75 76#ifdef DEBUG 77/** 78 * brcmf_fws_tlv_names - array of tlv names. 79 */ 80#define BRCMF_FWS_TLV_DEF(name, id, len) \ 81 { id, #name }, 82static struct { 83 enum brcmf_fws_tlv_type id; 84 const char *name; 85} brcmf_fws_tlv_names[] = { 86 BRCMF_FWS_TLV_DEFLIST 87}; 88#undef BRCMF_FWS_TLV_DEF 89 90static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) 91{ 92 int i; 93 94 for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++) 95 if (brcmf_fws_tlv_names[i].id == id) 96 return brcmf_fws_tlv_names[i].name; 97 98 return "INVALID"; 99} 100#else 101static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) 102{ 103 return "NODEBUG"; 104} 105#endif /* DEBUG */ 106 107/** 108 * flags used to enable tlv signalling from firmware. 109 */ 110#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001 111#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002 112#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004 113#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008 114#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010 115#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 116#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040 117 118#define BRCMF_FWS_HANGER_MAXITEMS 1024 119#define BRCMF_FWS_HANGER_ITEM_STATE_FREE 1 120#define BRCMF_FWS_HANGER_ITEM_STATE_INUSE 2 121#define BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED 3 122 123#define BRCMF_FWS_STATE_OPEN 1 124#define BRCMF_FWS_STATE_CLOSE 2 125 126#define BRCMF_FWS_FCMODE_NONE 0 127#define BRCMF_FWS_FCMODE_IMPLIED_CREDIT 1 128#define BRCMF_FWS_FCMODE_EXPLICIT_CREDIT 2 129 130#define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32 131#define BRCMF_FWS_MAX_IFNUM 16 132#define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff 133 134#define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0 135#define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1 136 137#define BRCMF_FWS_PSQ_PREC_COUNT ((NL80211_NUM_ACS + 1) * 2) 138#define BRCMF_FWS_PSQ_LEN 256 139 140/** 141 * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface 142 * 143 * @occupied: slot is in use. 144 * @mac_handle: handle for mac entry determined by firmware. 145 * @interface_id: interface index. 146 * @state: current state. 147 * @ac_bitmap: ac queue bitmap. 148 * @requested_credit: credits requested by firmware. 149 * @ea: ethernet address. 150 * @psq: power-save queue. 151 */ 152struct brcmf_fws_mac_descriptor { 153 u8 occupied; 154 u8 mac_handle; 155 u8 interface_id; 156 u8 state; 157 u8 ac_bitmap; 158 u8 requested_credit; 159 u8 ea[ETH_ALEN]; 160 struct pktq psq; 161}; 162 163/** 164 * FWFC packet identifier 165 * 166 * 32-bit packet identifier used in PKTTAG tlv from host to dongle. 167 * 168 * - Generated at the host (e.g. dhd) 169 * - Seen as a generic sequence number by wlc except the flags field 170 * 171 * Generation : b[31] => generation number for this packet [host->fw] 172 * OR, current generation number [fw->host] 173 * Flags : b[30:27] => command, status flags 174 * FIFO-AC : b[26:24] => AC-FIFO id 175 * h-slot : b[23:8] => hanger-slot 176 * freerun : b[7:0] => A free running counter 177 */ 178#define BRCMF_FWS_PKTTAG_GENERATION_MASK 0x80000000 179#define BRCMF_FWS_PKTTAG_GENERATION_SHIFT 31 180#define BRCMF_FWS_PKTTAG_FLAGS_MASK 0x78000000 181#define BRCMF_FWS_PKTTAG_FLAGS_SHIFT 27 182#define BRCMF_FWS_PKTTAG_FIFO_MASK 0x07000000 183#define BRCMF_FWS_PKTTAG_FIFO_SHIFT 24 184#define BRCMF_FWS_PKTTAG_HSLOT_MASK 0x00ffff00 185#define BRCMF_FWS_PKTTAG_HSLOT_SHIFT 8 186#define BRCMF_FWS_PKTTAG_FREERUN_MASK 0x000000ff 187#define BRCMF_FWS_PKTTAG_FREERUN_SHIFT 0 188 189#define brcmf_fws_pkttag_set_field(var, field, value) \ 190 brcmu_maskset32((var), BRCMF_FWS_PKTTAG_ ## field ## _MASK, \ 191 BRCMF_FWS_PKTTAG_ ## field ## _SHIFT, (value)) 192#define brcmf_fws_pkttag_get_field(var, field) \ 193 brcmu_maskget32((var), BRCMF_FWS_PKTTAG_ ## field ## _MASK, \ 194 BRCMF_FWS_PKTTAG_ ## field ## _SHIFT) 195 196struct brcmf_fws_info { 197 struct brcmf_pub *drvr; 198 struct brcmf_fws_stats stats; 199 struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE]; 200 int fifo_credit[NL80211_NUM_ACS+1+1]; 201}; 202 203/** 204 * brcmf_fws_get_tlv_len() - returns defined length for given tlv id. 205 */ 206#define BRCMF_FWS_TLV_DEF(name, id, len) \ 207 case BRCMF_FWS_TYPE_ ## name: \ 208 return len; 209 210static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws, 211 enum brcmf_fws_tlv_type id) 212{ 213 switch (id) { 214 BRCMF_FWS_TLV_DEFLIST 215 default: 216 brcmf_err("invalid tlv id: %d\n", id); 217 fws->stats.tlv_invalid_type++; 218 break; 219 } 220 return -EINVAL; 221} 222#undef BRCMF_FWS_TLV_DEF 223 224static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc, 225 u8 *addr, u8 ifidx) 226{ 227 brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u\n", addr, ifidx); 228 desc->occupied = 1; 229 desc->state = BRCMF_FWS_STATE_OPEN; 230 desc->requested_credit = 0; 231 /* depending on use may need ifp->bssidx instead */ 232 desc->interface_id = ifidx; 233 desc->ac_bitmap = 0xff; /* update this when handling APSD */ 234 memcpy(&desc->ea[0], addr, ETH_ALEN); 235} 236 237static 238void brcmf_fws_clear_mac_descriptor(struct brcmf_fws_mac_descriptor *desc) 239{ 240 brcmf_dbg(TRACE, 241 "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id); 242 desc->occupied = 0; 243 desc->state = BRCMF_FWS_STATE_CLOSE; 244 desc->requested_credit = 0; 245} 246 247static struct brcmf_fws_mac_descriptor * 248brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea) 249{ 250 struct brcmf_fws_mac_descriptor *entry; 251 int i; 252 253 brcmf_dbg(TRACE, "enter: ea=%pM\n", ea); 254 if (ea == NULL) 255 return ERR_PTR(-EINVAL); 256 257 entry = &fws->nodes[0]; 258 for (i = 0; i < ARRAY_SIZE(fws->nodes); i++) { 259 if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN)) 260 return entry; 261 entry++; 262 } 263 264 return ERR_PTR(-ENOENT); 265} 266 267static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) 268{ 269 brcmf_dbg(CTL, "rssi %d\n", rssi); 270 return 0; 271} 272 273static 274int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) 275{ 276 struct brcmf_fws_mac_descriptor *entry, *existing; 277 u8 mac_handle; 278 u8 ifidx; 279 u8 *addr; 280 281 mac_handle = *data++; 282 ifidx = *data++; 283 addr = data; 284 285 entry = &fws->nodes[mac_handle & 0x1F]; 286 if (type == BRCMF_FWS_TYPE_MACDESC_DEL) { 287 brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx); 288 if (entry->occupied) { 289 entry->occupied = 0; 290 entry->state = BRCMF_FWS_STATE_CLOSE; 291 entry->requested_credit = 0; 292 } else { 293 fws->stats.mac_update_failed++; 294 } 295 return 0; 296 } 297 298 brcmf_dbg(TRACE, "add mac %pM idx %d\n", addr, ifidx); 299 existing = brcmf_fws_mac_descriptor_lookup(fws, addr); 300 if (IS_ERR(existing)) { 301 if (!entry->occupied) { 302 entry->mac_handle = mac_handle; 303 brcmf_fws_init_mac_descriptor(entry, addr, ifidx); 304 brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, 305 BRCMF_FWS_PSQ_LEN); 306 } else { 307 fws->stats.mac_update_failed++; 308 } 309 } else { 310 if (entry != existing) { 311 brcmf_dbg(TRACE, "relocate mac\n"); 312 memcpy(entry, existing, 313 offsetof(struct brcmf_fws_mac_descriptor, psq)); 314 entry->mac_handle = mac_handle; 315 brcmf_fws_clear_mac_descriptor(existing); 316 } else { 317 brcmf_dbg(TRACE, "use existing\n"); 318 WARN_ON(entry->mac_handle != mac_handle); 319 /* TODO: what should we do here: continue, reinit, .. */ 320 } 321 } 322 return 0; 323} 324 325static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) 326{ 327 __le32 timestamp; 328 329 memcpy(×tamp, &data[2], sizeof(timestamp)); 330 brcmf_dbg(INFO, "received: seq %d, timestamp %d\n", data[1], 331 le32_to_cpu(timestamp)); 332 return 0; 333} 334 335/* using macro so sparse checking does not complain 336 * about locking imbalance. 337 */ 338#define brcmf_fws_lock(drvr, flags) \ 339do { \ 340 flags = 0; \ 341 spin_lock_irqsave(&((drvr)->fws_spinlock), (flags)); \ 342} while (0) 343 344/* using macro so sparse checking does not complain 345 * about locking imbalance. 346 */ 347#define brcmf_fws_unlock(drvr, flags) \ 348 spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags)) 349 350static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, 351 const struct brcmf_event_msg *e, 352 void *data) 353{ 354 struct brcmf_fws_info *fws = ifp->drvr->fws; 355 int i; 356 ulong flags; 357 u8 *credits = data; 358 359 brcmf_fws_lock(ifp->drvr, flags); 360 for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) 361 fws->fifo_credit[i] = *credits++; 362 brcmf_fws_unlock(ifp->drvr, flags); 363 return 0; 364} 365 366int brcmf_fws_init(struct brcmf_pub *drvr) 367{ 368 u32 tlv = 0; 369 int rc; 370 371 /* enable rssi signals */ 372 if (drvr->fw_signals) 373 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS | 374 BRCMF_FWS_FLAGS_XONXOFF_SIGNALS; 375 376 spin_lock_init(&drvr->fws_spinlock); 377 378 drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); 379 if (!drvr->fws) { 380 rc = -ENOMEM; 381 goto fail; 382 } 383 384 /* enable proptxtstatus signaling by default */ 385 rc = brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv); 386 if (rc < 0) { 387 brcmf_err("failed to set bdcv2 tlv signaling\n"); 388 goto fail; 389 } 390 391 if (brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP, 392 brcmf_fws_notify_credit_map)) { 393 brcmf_err("register credit map handler failed\n"); 394 goto fail; 395 } 396 397 /* set linkage back */ 398 drvr->fws->drvr = drvr; 399 400 /* create debugfs file for statistics */ 401 brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); 402 403 /* TODO: remove upon feature delivery */ 404 brcmf_err("%s bdcv2 tlv signaling [%x]\n", 405 drvr->fw_signals ? "enabled" : "disabled", tlv); 406 return 0; 407 408fail: 409 /* disable flow control entirely */ 410 drvr->fw_signals = false; 411 brcmf_fws_deinit(drvr); 412 return rc; 413} 414 415void brcmf_fws_deinit(struct brcmf_pub *drvr) 416{ 417 /* free top structure */ 418 kfree(drvr->fws); 419 drvr->fws = NULL; 420} 421 422int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, 423 struct sk_buff *skb) 424{ 425 struct brcmf_fws_info *fws = drvr->fws; 426 ulong flags; 427 u8 *signal_data; 428 s16 data_len; 429 u8 type; 430 u8 len; 431 u8 *data; 432 433 brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n", 434 ifidx, skb->len, signal_len); 435 436 WARN_ON(signal_len > skb->len); 437 438 /* if flow control disabled, skip to packet data and leave */ 439 if (!signal_len || !drvr->fw_signals) { 440 skb_pull(skb, signal_len); 441 return 0; 442 } 443 444 /* lock during tlv parsing */ 445 brcmf_fws_lock(drvr, flags); 446 447 fws->stats.header_pulls++; 448 data_len = signal_len; 449 signal_data = skb->data; 450 451 while (data_len > 0) { 452 /* extract tlv info */ 453 type = signal_data[0]; 454 455 /* FILLER type is actually not a TLV, but 456 * a single byte that can be skipped. 457 */ 458 if (type == BRCMF_FWS_TYPE_FILLER) { 459 signal_data += 1; 460 data_len -= 1; 461 continue; 462 } 463 len = signal_data[1]; 464 data = signal_data + 2; 465 466 /* abort parsing when length invalid */ 467 if (data_len < len + 2) 468 break; 469 470 if (len != brcmf_fws_get_tlv_len(fws, type)) 471 break; 472 473 brcmf_dbg(INFO, "tlv type=%d (%s), len=%d\n", type, 474 brcmf_fws_get_tlv_name(type), len); 475 switch (type) { 476 case BRCMF_FWS_TYPE_MAC_OPEN: 477 case BRCMF_FWS_TYPE_MAC_CLOSE: 478 case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT: 479 case BRCMF_FWS_TYPE_TXSTATUS: 480 case BRCMF_FWS_TYPE_PKTTAG: 481 case BRCMF_FWS_TYPE_INTERFACE_OPEN: 482 case BRCMF_FWS_TYPE_INTERFACE_CLOSE: 483 case BRCMF_FWS_TYPE_FIFO_CREDITBACK: 484 case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP: 485 case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET: 486 case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS: 487 case BRCMF_FWS_TYPE_COMP_TXSTATUS: 488 break; 489 case BRCMF_FWS_TYPE_MACDESC_ADD: 490 case BRCMF_FWS_TYPE_MACDESC_DEL: 491 brcmf_fws_macdesc_indicate(fws, type, data); 492 break; 493 case BRCMF_FWS_TYPE_RSSI: 494 brcmf_fws_rssi_indicate(fws, *data); 495 break; 496 case BRCMF_FWS_TYPE_TRANS_ID: 497 brcmf_fws_dbg_seqnum_check(fws, data); 498 break; 499 default: 500 fws->stats.tlv_invalid_type++; 501 break; 502 } 503 504 signal_data += len + 2; 505 data_len -= len + 2; 506 } 507 508 if (data_len != 0) 509 fws->stats.tlv_parse_failed++; 510 511 /* signalling processing result does 512 * not affect the actual ethernet packet. 513 */ 514 skb_pull(skb, signal_len); 515 516 /* this may be a signal-only packet 517 */ 518 if (skb->len == 0) 519 fws->stats.header_only_pkt++; 520 521 brcmf_fws_unlock(drvr, flags); 522 return 0; 523} 524 525void brcmf_fws_reset_interface(struct brcmf_if *ifp) 526{ 527 struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; 528 529 brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); 530 if (!entry) 531 return; 532 533 brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx); 534} 535 536void brcmf_fws_add_interface(struct brcmf_if *ifp) 537{ 538 struct brcmf_fws_mac_descriptor *entry; 539 540 brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n", 541 ifp->bssidx, ifp->mac_addr); 542 if (!ifp->drvr->fw_signals) 543 return; 544 545 entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 546 if (entry) { 547 ifp->fws_desc = entry; 548 brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx); 549 brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, 550 BRCMF_FWS_PSQ_LEN); 551 } else { 552 brcmf_err("no firmware signalling\n"); 553 } 554} 555 556void brcmf_fws_del_interface(struct brcmf_if *ifp) 557{ 558 struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; 559 560 brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); 561 if (!entry) 562 return; 563 564 ifp->fws_desc = NULL; 565 brcmf_fws_clear_mac_descriptor(entry); 566 kfree(entry); 567} 568