]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
7ceaceba18457119076aee2a183db0eed864b434
[karo-tx-linux.git] / drivers / net / wireless / brcm80211 / brcmfmac / fwsignal.c
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,
70 enum 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 },
82 static 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
90 static 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
101 static 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  * @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  */
151 struct 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
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
194 struct brcmf_fws_info {
195         struct brcmf_pub *drvr;
196         struct brcmf_fws_stats stats;
197 };
198
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
206 static 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
220 static 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
226 static 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)                             \
240 do {                                                            \
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
251 int 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
284 fail:
285         /* disable flow control entirely */
286         drvr->fw_signals = false;
287         brcmf_fws_deinit(drvr);
288         return rc;
289 }
290
291 void brcmf_fws_deinit(struct brcmf_pub *drvr)
292 {
293         /* free top structure */
294         kfree(drvr->fws);
295         drvr->fws = NULL;
296 }
297
298 int 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
346                 if (len != brcmf_fws_get_tlv_len(fws, type))
347                         break;
348
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:
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:
366                         break;
367                 case BRCMF_FWS_TYPE_RSSI:
368                         brcmf_fws_rssi_indicate(fws, *data);
369                         break;
370                 case BRCMF_FWS_TYPE_TRANS_ID:
371                         brcmf_fws_dbg_seqnum_check(fws, data);
372                         break;
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 }
398
399 void 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
416 void 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
436 void 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 }