fwsignal.c revision 349e7104ff662eeacca1fffbb480c287562c45a1
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/** 77 * enum brcmf_fws_tlv_len - length values for tlvs. 78 */ 79#define BRCMF_FWS_TLV_DEF(name, id, len) \ 80 BRCMF_FWS_TYPE_ ## name ## _LEN = len, 81enum brcmf_fws_tlv_len { 82 BRCMF_FWS_TLV_DEFLIST 83}; 84#undef BRCMF_FWS_TLV_DEF 85 86#ifdef DEBUG 87/** 88 * brcmf_fws_tlv_names - array of tlv names. 89 */ 90#define BRCMF_FWS_TLV_DEF(name, id, len) \ 91 { id, #name }, 92static struct { 93 enum brcmf_fws_tlv_type id; 94 const char *name; 95} brcmf_fws_tlv_names[] = { 96 BRCMF_FWS_TLV_DEFLIST 97}; 98#undef BRCMF_FWS_TLV_DEF 99 100static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) 101{ 102 int i; 103 104 for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++) 105 if (brcmf_fws_tlv_names[i].id == id) 106 return brcmf_fws_tlv_names[i].name; 107 108 return "INVALID"; 109} 110#else 111static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) 112{ 113 return "NODEBUG"; 114} 115#endif /* DEBUG */ 116 117/** 118 * flags used to enable tlv signalling from firmware. 119 */ 120#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001 121#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002 122#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004 123#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008 124#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010 125#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 126#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040 127 128#define BRCMF_FWS_HANGER_MAXITEMS 1024 129#define BRCMF_FWS_HANGER_ITEM_STATE_FREE 1 130#define BRCMF_FWS_HANGER_ITEM_STATE_INUSE 2 131#define BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED 3 132 133#define BRCMF_FWS_STATE_OPEN 1 134#define BRCMF_FWS_STATE_CLOSE 2 135 136#define BRCMF_FWS_FCMODE_NONE 0 137#define BRCMF_FWS_FCMODE_IMPLIED_CREDIT 1 138#define BRCMF_FWS_FCMODE_EXPLICIT_CREDIT 2 139 140#define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32 141#define BRCMF_FWS_MAX_IFNUM 16 142#define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff 143 144#define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0 145#define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1 146 147/** 148 * FWFC packet identifier 149 * 150 * 32-bit packet identifier used in PKTTAG tlv from host to dongle. 151 * 152 * - Generated at the host (e.g. dhd) 153 * - Seen as a generic sequence number by wlc except the flags field 154 * 155 * Generation : b[31] => generation number for this packet [host->fw] 156 * OR, current generation number [fw->host] 157 * Flags : b[30:27] => command, status flags 158 * FIFO-AC : b[26:24] => AC-FIFO id 159 * h-slot : b[23:8] => hanger-slot 160 * freerun : b[7:0] => A free running counter 161 */ 162#define BRCMF_FWS_PKTTAG_GENERATION_MASK 0x80000000 163#define BRCMF_FWS_PKTTAG_GENERATION_SHIFT 31 164#define BRCMF_FWS_PKTTAG_FLAGS_MASK 0x78000000 165#define BRCMF_FWS_PKTTAG_FLAGS_SHIFT 27 166#define BRCMF_FWS_PKTTAG_FIFO_MASK 0x07000000 167#define BRCMF_FWS_PKTTAG_FIFO_SHIFT 24 168#define BRCMF_FWS_PKTTAG_HSLOT_MASK 0x00ffff00 169#define BRCMF_FWS_PKTTAG_HSLOT_SHIFT 8 170#define BRCMF_FWS_PKTTAG_FREERUN_MASK 0x000000ff 171#define BRCMF_FWS_PKTTAG_FREERUN_SHIFT 0 172 173#define brcmf_fws_pkttag_set_field(var, field, value) \ 174 brcmu_maskset32((var), BRCMF_FWS_PKTTAG_ ## field ## _MASK, \ 175 BRCMF_FWS_PKTTAG_ ## field ## _SHIFT, (value)) 176#define brcmf_fws_pkttag_get_field(var, field) \ 177 brcmu_maskget32((var), BRCMF_FWS_PKTTAG_ ## field ## _MASK, \ 178 BRCMF_FWS_PKTTAG_ ## field ## _SHIFT) 179 180struct brcmf_fws_info { 181 struct brcmf_pub *drvr; 182 struct brcmf_fws_stats stats; 183}; 184 185static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) 186{ 187 brcmf_dbg(CTL, "rssi %d\n", rssi); 188 return 0; 189} 190 191static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) 192{ 193 __le32 timestamp; 194 195 memcpy(×tamp, &data[2], sizeof(timestamp)); 196 brcmf_dbg(INFO, "received: seq %d, timestamp %d\n", data[1], 197 le32_to_cpu(timestamp)); 198 return 0; 199} 200 201/* using macro so sparse checking does not complain 202 * about locking imbalance. 203 */ 204#define brcmf_fws_lock(drvr, flags) \ 205do { \ 206 flags = 0; \ 207 spin_lock_irqsave(&((drvr)->fws_spinlock), (flags)); \ 208} while (0) 209 210/* using macro so sparse checking does not complain 211 * about locking imbalance. 212 */ 213#define brcmf_fws_unlock(drvr, flags) \ 214 spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags)) 215 216int brcmf_fws_init(struct brcmf_pub *drvr) 217{ 218 u32 tlv; 219 int rc; 220 221 /* enable rssi signals */ 222 tlv = drvr->fw_signals ? BRCMF_FWS_FLAGS_RSSI_SIGNALS : 0; 223 224 spin_lock_init(&drvr->fws_spinlock); 225 226 drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); 227 if (!drvr->fws) { 228 rc = -ENOMEM; 229 goto fail; 230 } 231 232 /* enable proptxtstatus signaling by default */ 233 rc = brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv); 234 if (rc < 0) { 235 brcmf_err("failed to set bdcv2 tlv signaling\n"); 236 goto fail; 237 } 238 /* set linkage back */ 239 drvr->fws->drvr = drvr; 240 241 /* create debugfs file for statistics */ 242 brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); 243 244 /* TODO: remove upon feature delivery */ 245 brcmf_err("%s bdcv2 tlv signaling [%x]\n", 246 drvr->fw_signals ? "enabled" : "disabled", tlv); 247 return 0; 248 249fail: 250 /* disable flow control entirely */ 251 drvr->fw_signals = false; 252 brcmf_fws_deinit(drvr); 253 return rc; 254} 255 256void brcmf_fws_deinit(struct brcmf_pub *drvr) 257{ 258 /* free top structure */ 259 kfree(drvr->fws); 260 drvr->fws = NULL; 261} 262 263int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, 264 struct sk_buff *skb) 265{ 266 struct brcmf_fws_info *fws = drvr->fws; 267 ulong flags; 268 u8 *signal_data; 269 s16 data_len; 270 u8 type; 271 u8 len; 272 u8 *data; 273 274 brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n", 275 ifidx, skb->len, signal_len); 276 277 WARN_ON(signal_len > skb->len); 278 279 /* if flow control disabled, skip to packet data and leave */ 280 if (!signal_len || !drvr->fw_signals) { 281 skb_pull(skb, signal_len); 282 return 0; 283 } 284 285 /* lock during tlv parsing */ 286 brcmf_fws_lock(drvr, flags); 287 288 fws->stats.header_pulls++; 289 data_len = signal_len; 290 signal_data = skb->data; 291 292 while (data_len > 0) { 293 /* extract tlv info */ 294 type = signal_data[0]; 295 296 /* FILLER type is actually not a TLV, but 297 * a single byte that can be skipped. 298 */ 299 if (type == BRCMF_FWS_TYPE_FILLER) { 300 signal_data += 1; 301 data_len -= 1; 302 continue; 303 } 304 len = signal_data[1]; 305 data = signal_data + 2; 306 307 /* abort parsing when length invalid */ 308 if (data_len < len + 2) 309 break; 310 311 brcmf_dbg(INFO, "tlv type=%d (%s), len=%d\n", type, 312 brcmf_fws_get_tlv_name(type), len); 313 switch (type) { 314 case BRCMF_FWS_TYPE_MAC_OPEN: 315 case BRCMF_FWS_TYPE_MAC_CLOSE: 316 WARN_ON(len != BRCMF_FWS_TYPE_MAC_OPEN_LEN); 317 break; 318 case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT: 319 WARN_ON(len != BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT_LEN); 320 break; 321 case BRCMF_FWS_TYPE_TXSTATUS: 322 WARN_ON(len != BRCMF_FWS_TYPE_TXSTATUS_LEN); 323 break; 324 case BRCMF_FWS_TYPE_PKTTAG: 325 WARN_ON(len != BRCMF_FWS_TYPE_PKTTAG_LEN); 326 break; 327 case BRCMF_FWS_TYPE_MACDESC_ADD: 328 case BRCMF_FWS_TYPE_MACDESC_DEL: 329 WARN_ON(len != BRCMF_FWS_TYPE_MACDESC_ADD_LEN); 330 break; 331 case BRCMF_FWS_TYPE_RSSI: 332 WARN_ON(len != BRCMF_FWS_TYPE_RSSI_LEN); 333 brcmf_fws_rssi_indicate(fws, *(s8 *)data); 334 break; 335 case BRCMF_FWS_TYPE_INTERFACE_OPEN: 336 case BRCMF_FWS_TYPE_INTERFACE_CLOSE: 337 WARN_ON(len != BRCMF_FWS_TYPE_INTERFACE_OPEN_LEN); 338 break; 339 case BRCMF_FWS_TYPE_FIFO_CREDITBACK: 340 WARN_ON(len != BRCMF_FWS_TYPE_FIFO_CREDITBACK_LEN); 341 break; 342 case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP: 343 WARN_ON(len != BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN); 344 break; 345 case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET: 346 WARN_ON(len != BRCMF_FWS_TYPE_MAC_REQUEST_PACKET_LEN); 347 break; 348 case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS: 349 WARN_ON(len != BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS_LEN); 350 break; 351 case BRCMF_FWS_TYPE_TRANS_ID: 352 WARN_ON(len != BRCMF_FWS_TYPE_TRANS_ID_LEN); 353 brcmf_fws_dbg_seqnum_check(fws, data); 354 break; 355 case BRCMF_FWS_TYPE_COMP_TXSTATUS: 356 WARN_ON(len != BRCMF_FWS_TYPE_COMP_TXSTATUS_LEN); 357 break; 358 default: 359 fws->stats.tlv_invalid_type++; 360 break; 361 } 362 363 signal_data += len + 2; 364 data_len -= len + 2; 365 } 366 367 if (data_len != 0) 368 fws->stats.tlv_parse_failed++; 369 370 /* signalling processing result does 371 * not affect the actual ethernet packet. 372 */ 373 skb_pull(skb, signal_len); 374 375 /* this may be a signal-only packet 376 */ 377 if (skb->len == 0) 378 fws->stats.header_only_pkt++; 379 380 brcmf_fws_unlock(drvr, flags); 381 return 0; 382} 383