brcmfmac: hookup firmware signalling to firmware interface events
[deliverable/linux.git] / drivers / net / wireless / brcm80211 / brcmfmac / fwsignal.c
CommitLineData
349e7104
AS
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
349e7104
AS
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 */
bb8c8063
AS
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
349e7104
AS
124#define BRCMF_FWS_STATE_CLOSE 2
125
126#define BRCMF_FWS_FCMODE_NONE 0
127#define BRCMF_FWS_FCMODE_IMPLIED_CREDIT 1
bb8c8063 128#define BRCMF_FWS_FCMODE_EXPLICIT_CREDIT 2
349e7104
AS
129
130#define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32
bb8c8063 131#define BRCMF_FWS_MAX_IFNUM 16
349e7104
AS
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
bb8c8063
AS
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 * @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.
150 */
151struct brcmf_fws_mac_descriptor {
152 u8 occupied;
153 u8 interface_id;
154 u8 state;
155 u8 ac_bitmap;
156 u8 requested_credit;
157 u8 ea[ETH_ALEN];
158 struct pktq psq;
159};
160
349e7104
AS
161/**
162 * FWFC packet identifier
163 *
164 * 32-bit packet identifier used in PKTTAG tlv from host to dongle.
165 *
166 * - Generated at the host (e.g. dhd)
167 * - Seen as a generic sequence number by wlc except the flags field
168 *
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
175 */
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
186
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)
193
194struct brcmf_fws_info {
195 struct brcmf_pub *drvr;
196 struct brcmf_fws_stats stats;
197};
198
bb8c8063
AS
199/**
200 * brcmf_fws_get_tlv_len() - returns defined length for given tlv id.
201 */
202#define BRCMF_FWS_TLV_DEF(name, id, len) \
203 case BRCMF_FWS_TYPE_ ## name: \
204 return len;
205
206static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
207 enum brcmf_fws_tlv_type id)
208{
209 switch (id) {
210 BRCMF_FWS_TLV_DEFLIST
211 default:
212 brcmf_err("invalid tlv id: %d\n", id);
213 fws->stats.tlv_invalid_type++;
214 break;
215 }
216 return -EINVAL;
217}
218#undef BRCMF_FWS_TLV_DEF
219
349e7104
AS
220static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
221{
222 brcmf_dbg(CTL, "rssi %d\n", rssi);
223 return 0;
224}
225
226static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
227{
228 __le32 timestamp;
229
230 memcpy(&timestamp, &data[2], sizeof(timestamp));
231 brcmf_dbg(INFO, "received: seq %d, timestamp %d\n", data[1],
232 le32_to_cpu(timestamp));
233 return 0;
234}
235
236/* using macro so sparse checking does not complain
237 * about locking imbalance.
238 */
239#define brcmf_fws_lock(drvr, flags) \
240do { \
241 flags = 0; \
242 spin_lock_irqsave(&((drvr)->fws_spinlock), (flags)); \
243} while (0)
244
245/* using macro so sparse checking does not complain
246 * about locking imbalance.
247 */
248#define brcmf_fws_unlock(drvr, flags) \
249 spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags))
250
251int brcmf_fws_init(struct brcmf_pub *drvr)
252{
253 u32 tlv;
254 int rc;
255
256 /* enable rssi signals */
257 tlv = drvr->fw_signals ? BRCMF_FWS_FLAGS_RSSI_SIGNALS : 0;
258
259 spin_lock_init(&drvr->fws_spinlock);
260
261 drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
262 if (!drvr->fws) {
263 rc = -ENOMEM;
264 goto fail;
265 }
266
267 /* enable proptxtstatus signaling by default */
268 rc = brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv);
269 if (rc < 0) {
270 brcmf_err("failed to set bdcv2 tlv signaling\n");
271 goto fail;
272 }
273 /* set linkage back */
274 drvr->fws->drvr = drvr;
275
276 /* create debugfs file for statistics */
277 brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats);
278
279 /* TODO: remove upon feature delivery */
280 brcmf_err("%s bdcv2 tlv signaling [%x]\n",
281 drvr->fw_signals ? "enabled" : "disabled", tlv);
282 return 0;
283
284fail:
285 /* disable flow control entirely */
286 drvr->fw_signals = false;
287 brcmf_fws_deinit(drvr);
288 return rc;
289}
290
291void brcmf_fws_deinit(struct brcmf_pub *drvr)
292{
293 /* free top structure */
294 kfree(drvr->fws);
295 drvr->fws = NULL;
296}
297
298int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
299 struct sk_buff *skb)
300{
301 struct brcmf_fws_info *fws = drvr->fws;
302 ulong flags;
303 u8 *signal_data;
304 s16 data_len;
305 u8 type;
306 u8 len;
307 u8 *data;
308
309 brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n",
310 ifidx, skb->len, signal_len);
311
312 WARN_ON(signal_len > skb->len);
313
314 /* if flow control disabled, skip to packet data and leave */
315 if (!signal_len || !drvr->fw_signals) {
316 skb_pull(skb, signal_len);
317 return 0;
318 }
319
320 /* lock during tlv parsing */
321 brcmf_fws_lock(drvr, flags);
322
323 fws->stats.header_pulls++;
324 data_len = signal_len;
325 signal_data = skb->data;
326
327 while (data_len > 0) {
328 /* extract tlv info */
329 type = signal_data[0];
330
331 /* FILLER type is actually not a TLV, but
332 * a single byte that can be skipped.
333 */
334 if (type == BRCMF_FWS_TYPE_FILLER) {
335 signal_data += 1;
336 data_len -= 1;
337 continue;
338 }
339 len = signal_data[1];
340 data = signal_data + 2;
341
342 /* abort parsing when length invalid */
343 if (data_len < len + 2)
344 break;
345
bb8c8063
AS
346 if (len != brcmf_fws_get_tlv_len(fws, type))
347 break;
348
349e7104
AS
349 brcmf_dbg(INFO, "tlv type=%d (%s), len=%d\n", type,
350 brcmf_fws_get_tlv_name(type), len);
351 switch (type) {
352 case BRCMF_FWS_TYPE_MAC_OPEN:
353 case BRCMF_FWS_TYPE_MAC_CLOSE:
349e7104 354 case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
349e7104 355 case BRCMF_FWS_TYPE_TXSTATUS:
349e7104 356 case BRCMF_FWS_TYPE_PKTTAG:
349e7104
AS
357 case BRCMF_FWS_TYPE_INTERFACE_OPEN:
358 case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
349e7104 359 case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
349e7104 360 case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP:
349e7104 361 case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
349e7104 362 case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
bb8c8063
AS
363 case BRCMF_FWS_TYPE_COMP_TXSTATUS:
364 case BRCMF_FWS_TYPE_MACDESC_ADD:
365 case BRCMF_FWS_TYPE_MACDESC_DEL:
366 break;
367 case BRCMF_FWS_TYPE_RSSI:
368 brcmf_fws_rssi_indicate(fws, *data);
349e7104
AS
369 break;
370 case BRCMF_FWS_TYPE_TRANS_ID:
349e7104
AS
371 brcmf_fws_dbg_seqnum_check(fws, data);
372 break;
349e7104
AS
373 default:
374 fws->stats.tlv_invalid_type++;
375 break;
376 }
377
378 signal_data += len + 2;
379 data_len -= len + 2;
380 }
381
382 if (data_len != 0)
383 fws->stats.tlv_parse_failed++;
384
385 /* signalling processing result does
386 * not affect the actual ethernet packet.
387 */
388 skb_pull(skb, signal_len);
389
390 /* this may be a signal-only packet
391 */
392 if (skb->len == 0)
393 fws->stats.header_only_pkt++;
394
395 brcmf_fws_unlock(drvr, flags);
396 return 0;
397}
bb8c8063
AS
398
399void brcmf_fws_reset_interface(struct brcmf_if *ifp)
400{
401 struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
402
403 brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
404 if (!entry)
405 return;
406
407 entry->occupied = 1;
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);
414}
415
416void brcmf_fws_add_interface(struct brcmf_if *ifp)
417{
418 struct brcmf_fws_mac_descriptor *entry;
419
420 brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n",
421 ifp->bssidx, ifp->mac_addr);
422 if (!ifp->drvr->fw_signals)
423 return;
424
425 entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
426 if (entry) {
427 ifp->fws_desc = entry;
428 brcmf_fws_reset_interface(ifp);
429 brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
430 BRCMF_FWS_PSQ_LEN);
431 } else {
432 brcmf_err("no firmware signalling\n");
433 }
434}
435
436void brcmf_fws_del_interface(struct brcmf_if *ifp)
437{
438 struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
439
440 brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
441 if (!entry)
442 return;
443
444 ifp->fws_desc = NULL;
445 entry->occupied = 0;
446 entry->state = BRCMF_FWS_STATE_CLOSE;
447 entry->requested_credit = 0;
448 kfree(entry);
449}
This page took 0.048966 seconds and 5 git commands to generate.