2 * Copyright (c) 2010 Broadcom Corporation
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.
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.
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>
24 #include <brcmu_utils.h>
25 #include <brcmu_wifi.h>
33 * DOC: Firmware Signalling
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.
41 * single definition for firmware-driver flow control tlv's.
43 * each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length).
44 * A length value 0 indicates variable length tlv.
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)
66 * enum brcmf_fws_tlv_type - definition of tlv identifiers.
68 #define BRCMF_FWS_TLV_DEF(name, id, len) \
69 BRCMF_FWS_TYPE_ ## name = id,
70 enum brcmf_fws_tlv_type {
72 BRCMF_FWS_TYPE_INVALID
74 #undef BRCMF_FWS_TLV_DEF
78 * brcmf_fws_tlv_names - array of tlv names.
80 #define BRCMF_FWS_TLV_DEF(name, id, len) \
83 enum brcmf_fws_tlv_type id;
85 } brcmf_fws_tlv_names[] = {
88 #undef BRCMF_FWS_TLV_DEF
90 static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
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;
101 static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
108 * flags used to enable tlv signalling from firmware.
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
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
123 #define BRCMF_FWS_STATE_OPEN 1
124 #define BRCMF_FWS_STATE_CLOSE 2
126 #define BRCMF_FWS_FCMODE_NONE 0
127 #define BRCMF_FWS_FCMODE_IMPLIED_CREDIT 1
128 #define BRCMF_FWS_FCMODE_EXPLICIT_CREDIT 2
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
134 #define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0
135 #define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1
137 #define BRCMF_FWS_PSQ_PREC_COUNT ((NL80211_NUM_ACS + 1) * 2)
138 #define BRCMF_FWS_PSQ_LEN 256
141 * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface
143 * @occupied: slot is in use.
144 * @interface_id: interface index.
145 * @state: current state.
146 * @ac_bitmap: ac queue bitmap.
147 * @requested_credit: credits requested by firmware.
148 * @ea: ethernet address.
149 * @psq: power-save queue.
151 struct brcmf_fws_mac_descriptor {
162 * FWFC packet identifier
164 * 32-bit packet identifier used in PKTTAG tlv from host to dongle.
166 * - Generated at the host (e.g. dhd)
167 * - Seen as a generic sequence number by wlc except the flags field
169 * Generation : b[31] => generation number for this packet [host->fw]
170 * OR, current generation number [fw->host]
171 * Flags : b[30:27] => command, status flags
172 * FIFO-AC : b[26:24] => AC-FIFO id
173 * h-slot : b[23:8] => hanger-slot
174 * freerun : b[7:0] => A free running counter
176 #define BRCMF_FWS_PKTTAG_GENERATION_MASK 0x80000000
177 #define BRCMF_FWS_PKTTAG_GENERATION_SHIFT 31
178 #define BRCMF_FWS_PKTTAG_FLAGS_MASK 0x78000000
179 #define BRCMF_FWS_PKTTAG_FLAGS_SHIFT 27
180 #define BRCMF_FWS_PKTTAG_FIFO_MASK 0x07000000
181 #define BRCMF_FWS_PKTTAG_FIFO_SHIFT 24
182 #define BRCMF_FWS_PKTTAG_HSLOT_MASK 0x00ffff00
183 #define BRCMF_FWS_PKTTAG_HSLOT_SHIFT 8
184 #define BRCMF_FWS_PKTTAG_FREERUN_MASK 0x000000ff
185 #define BRCMF_FWS_PKTTAG_FREERUN_SHIFT 0
187 #define brcmf_fws_pkttag_set_field(var, field, value) \
188 brcmu_maskset32((var), BRCMF_FWS_PKTTAG_ ## field ## _MASK, \
189 BRCMF_FWS_PKTTAG_ ## field ## _SHIFT, (value))
190 #define brcmf_fws_pkttag_get_field(var, field) \
191 brcmu_maskget32((var), BRCMF_FWS_PKTTAG_ ## field ## _MASK, \
192 BRCMF_FWS_PKTTAG_ ## field ## _SHIFT)
194 struct brcmf_fws_info {
195 struct brcmf_pub *drvr;
196 struct brcmf_fws_stats stats;
200 * brcmf_fws_get_tlv_len() - returns defined length for given tlv id.
202 #define BRCMF_FWS_TLV_DEF(name, id, len) \
203 case BRCMF_FWS_TYPE_ ## name: \
206 static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
207 enum brcmf_fws_tlv_type id)
210 BRCMF_FWS_TLV_DEFLIST
212 brcmf_err("invalid tlv id: %d\n", id);
213 fws->stats.tlv_invalid_type++;
218 #undef BRCMF_FWS_TLV_DEF
220 static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
222 brcmf_dbg(CTL, "rssi %d\n", rssi);
226 static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
230 memcpy(×tamp, &data[2], sizeof(timestamp));
231 brcmf_dbg(INFO, "received: seq %d, timestamp %d\n", data[1],
232 le32_to_cpu(timestamp));
236 /* using macro so sparse checking does not complain
237 * about locking imbalance.
239 #define brcmf_fws_lock(drvr, flags) \
242 spin_lock_irqsave(&((drvr)->fws_spinlock), (flags)); \
245 /* using macro so sparse checking does not complain
246 * about locking imbalance.
248 #define brcmf_fws_unlock(drvr, flags) \
249 spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags))
251 int brcmf_fws_init(struct brcmf_pub *drvr)
256 /* enable rssi signals */
257 tlv = drvr->fw_signals ? BRCMF_FWS_FLAGS_RSSI_SIGNALS : 0;
259 spin_lock_init(&drvr->fws_spinlock);
261 drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
267 /* enable proptxtstatus signaling by default */
268 rc = brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv);
270 brcmf_err("failed to set bdcv2 tlv signaling\n");
273 /* set linkage back */
274 drvr->fws->drvr = drvr;
276 /* create debugfs file for statistics */
277 brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats);
279 /* TODO: remove upon feature delivery */
280 brcmf_err("%s bdcv2 tlv signaling [%x]\n",
281 drvr->fw_signals ? "enabled" : "disabled", tlv);
285 /* disable flow control entirely */
286 drvr->fw_signals = false;
287 brcmf_fws_deinit(drvr);
291 void brcmf_fws_deinit(struct brcmf_pub *drvr)
293 /* free top structure */
298 int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
301 struct brcmf_fws_info *fws = drvr->fws;
309 brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n",
310 ifidx, skb->len, signal_len);
312 WARN_ON(signal_len > skb->len);
314 /* if flow control disabled, skip to packet data and leave */
315 if (!signal_len || !drvr->fw_signals) {
316 skb_pull(skb, signal_len);
320 /* lock during tlv parsing */
321 brcmf_fws_lock(drvr, flags);
323 fws->stats.header_pulls++;
324 data_len = signal_len;
325 signal_data = skb->data;
327 while (data_len > 0) {
328 /* extract tlv info */
329 type = signal_data[0];
331 /* FILLER type is actually not a TLV, but
332 * a single byte that can be skipped.
334 if (type == BRCMF_FWS_TYPE_FILLER) {
339 len = signal_data[1];
340 data = signal_data + 2;
342 /* abort parsing when length invalid */
343 if (data_len < len + 2)
346 if (len != brcmf_fws_get_tlv_len(fws, type))
349 brcmf_dbg(INFO, "tlv type=%d (%s), len=%d\n", type,
350 brcmf_fws_get_tlv_name(type), len);
352 case BRCMF_FWS_TYPE_MAC_OPEN:
353 case BRCMF_FWS_TYPE_MAC_CLOSE:
354 case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
355 case BRCMF_FWS_TYPE_TXSTATUS:
356 case BRCMF_FWS_TYPE_PKTTAG:
357 case BRCMF_FWS_TYPE_INTERFACE_OPEN:
358 case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
359 case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
360 case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP:
361 case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
362 case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
363 case BRCMF_FWS_TYPE_COMP_TXSTATUS:
364 case BRCMF_FWS_TYPE_MACDESC_ADD:
365 case BRCMF_FWS_TYPE_MACDESC_DEL:
367 case BRCMF_FWS_TYPE_RSSI:
368 brcmf_fws_rssi_indicate(fws, *data);
370 case BRCMF_FWS_TYPE_TRANS_ID:
371 brcmf_fws_dbg_seqnum_check(fws, data);
374 fws->stats.tlv_invalid_type++;
378 signal_data += len + 2;
383 fws->stats.tlv_parse_failed++;
385 /* signalling processing result does
386 * not affect the actual ethernet packet.
388 skb_pull(skb, signal_len);
390 /* this may be a signal-only packet
393 fws->stats.header_only_pkt++;
395 brcmf_fws_unlock(drvr, flags);
399 void brcmf_fws_reset_interface(struct brcmf_if *ifp)
401 struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
403 brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
408 entry->state = BRCMF_FWS_STATE_OPEN;
409 entry->requested_credit = 0;
410 /* depending on use may need ifp->bssidx instead */
411 entry->interface_id = ifp->ifidx;
412 entry->ac_bitmap = 0xff; /* update this when handling APSD */
413 memcpy(&entry->ea[0], ifp->mac_addr, ETH_ALEN);
416 void brcmf_fws_add_interface(struct brcmf_if *ifp)
418 struct brcmf_fws_mac_descriptor *entry;
420 brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n",
421 ifp->bssidx, ifp->mac_addr);
422 if (!ifp->drvr->fw_signals)
425 entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
427 ifp->fws_desc = entry;
428 brcmf_fws_reset_interface(ifp);
429 brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
432 brcmf_err("no firmware signalling\n");
436 void brcmf_fws_del_interface(struct brcmf_if *ifp)
438 struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
440 brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
444 ifp->fws_desc = NULL;
446 entry->state = BRCMF_FWS_STATE_CLOSE;
447 entry->requested_credit = 0;