aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/Makefile1
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd.h7
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c8
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c41
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h13
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c28
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c14
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c382
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h25
9 files changed, 501 insertions, 18 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
index 74282739350d..598c8e2f8d2b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
@@ -26,6 +26,7 @@ brcmfmac-objs += \
26 wl_cfg80211.o \ 26 wl_cfg80211.o \
27 fwil.o \ 27 fwil.o \
28 fweh.o \ 28 fweh.o \
29 fwsignal.o \
29 p2p.o \ 30 p2p.o \
30 dhd_cdc.o \ 31 dhd_cdc.o \
31 dhd_common.o \ 32 dhd_common.o \
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index ef6f23be6d32..c7fa20846b32 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -501,6 +501,7 @@ struct brcmf_dcmd {
501/* Forward decls for struct brcmf_pub (see below) */ 501/* Forward decls for struct brcmf_pub (see below) */
502struct brcmf_proto; /* device communication protocol info */ 502struct brcmf_proto; /* device communication protocol info */
503struct brcmf_cfg80211_dev; /* cfg80211 device info */ 503struct brcmf_cfg80211_dev; /* cfg80211 device info */
504struct brcmf_fws_info; /* firmware signalling info */
504 505
505/* Common structure for module and instance linkage */ 506/* Common structure for module and instance linkage */
506struct brcmf_pub { 507struct brcmf_pub {
@@ -527,6 +528,10 @@ struct brcmf_pub {
527 unsigned char proto_buf[BRCMF_DCMD_MAXLEN]; 528 unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
528 529
529 struct brcmf_fweh_info fweh; 530 struct brcmf_fweh_info fweh;
531
532 bool fw_signals;
533 struct brcmf_fws_info *fws;
534 spinlock_t fws_spinlock;
530#ifdef DEBUG 535#ifdef DEBUG
531 struct dentry *dbgfs_dir; 536 struct dentry *dbgfs_dir;
532#endif 537#endif
@@ -582,7 +587,7 @@ extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
582 void *buf, uint len); 587 void *buf, uint len);
583 588
584/* Remove any protocol-specific data header. */ 589/* Remove any protocol-specific data header. */
585extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx, 590extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
586 struct sk_buff *rxp); 591 struct sk_buff *rxp);
587 592
588extern int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); 593extern int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
index 81e1be7051ca..8212d4384b1b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
@@ -28,6 +28,7 @@
28#include "dhd.h" 28#include "dhd.h"
29#include "dhd_proto.h" 29#include "dhd_proto.h"
30#include "dhd_bus.h" 30#include "dhd_bus.h"
31#include "fwsignal.h"
31#include "dhd_dbg.h" 32#include "dhd_dbg.h"
32 33
33struct brcmf_proto_cdc_dcmd { 34struct brcmf_proto_cdc_dcmd {
@@ -294,7 +295,7 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
294 BDC_SET_IF_IDX(h, ifidx); 295 BDC_SET_IF_IDX(h, ifidx);
295} 296}
296 297
297int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx, 298int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
298 struct sk_buff *pktbuf) 299 struct sk_buff *pktbuf)
299{ 300{
300 struct brcmf_proto_bdc_header *h; 301 struct brcmf_proto_bdc_header *h;
@@ -341,7 +342,10 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
341 pktbuf->priority = h->priority & BDC_PRIORITY_MASK; 342 pktbuf->priority = h->priority & BDC_PRIORITY_MASK;
342 343
343 skb_pull(pktbuf, BDC_HEADER_LEN); 344 skb_pull(pktbuf, BDC_HEADER_LEN);
344 skb_pull(pktbuf, h->data_offset << 2); 345 if (do_fws)
346 brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf);
347 else
348 skb_pull(pktbuf, h->data_offset << 2);
345 349
346 if (pktbuf->len == 0) 350 if (pktbuf->len == 0)
347 return -ENODATA; 351 return -ENODATA;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
index 50f293851982..ac792499b46a 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
@@ -124,3 +124,44 @@ void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
124 debugfs_create_file("counters", S_IRUGO, dentry, 124 debugfs_create_file("counters", S_IRUGO, dentry,
125 sdcnt, &brcmf_debugfs_sdio_counter_ops); 125 sdcnt, &brcmf_debugfs_sdio_counter_ops);
126} 126}
127
128static
129ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
130 size_t count, loff_t *ppos)
131{
132 struct brcmf_fws_stats *fwstats = f->private_data;
133 char buf[100];
134 int res;
135
136 /* only allow read from start */
137 if (*ppos > 0)
138 return 0;
139
140 res = scnprintf(buf, sizeof(buf),
141 "header_pulls: %u\n"
142 "header_only_pkt: %u\n"
143 "tlv_parse_failed: %u\n"
144 "tlv_invalid_type: %u\n",
145 fwstats->header_pulls,
146 fwstats->header_only_pkt,
147 fwstats->tlv_parse_failed,
148 fwstats->tlv_invalid_type);
149
150 return simple_read_from_buffer(data, count, ppos, buf, res);
151}
152
153static const struct file_operations brcmf_debugfs_fws_stats_ops = {
154 .owner = THIS_MODULE,
155 .open = simple_open,
156 .read = brcmf_debugfs_fws_stats_read
157};
158
159void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
160 struct brcmf_fws_stats *stats)
161{
162 struct dentry *dentry = drvr->dbgfs_dir;
163
164 if (!IS_ERR_OR_NULL(dentry))
165 debugfs_create_file("fws_stats", S_IRUGO, dentry,
166 stats, &brcmf_debugfs_fws_stats_ops);
167}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
index 0a1806f58676..4bc646bde16f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
@@ -132,6 +132,13 @@ struct brcmf_sdio_count {
132 ulong rx_readahead_cnt; /* packets where header read-ahead was used */ 132 ulong rx_readahead_cnt; /* packets where header read-ahead was used */
133}; 133};
134 134
135struct brcmf_fws_stats {
136 u32 tlv_parse_failed;
137 u32 tlv_invalid_type;
138 u32 header_only_pkt;
139 u32 header_pulls;
140};
141
135struct brcmf_pub; 142struct brcmf_pub;
136#ifdef DEBUG 143#ifdef DEBUG
137void brcmf_debugfs_init(void); 144void brcmf_debugfs_init(void);
@@ -141,6 +148,8 @@ void brcmf_debugfs_detach(struct brcmf_pub *drvr);
141struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); 148struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
142void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, 149void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
143 struct brcmf_sdio_count *sdcnt); 150 struct brcmf_sdio_count *sdcnt);
151void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
152 struct brcmf_fws_stats *stats);
144#else 153#else
145static inline void brcmf_debugfs_init(void) 154static inline void brcmf_debugfs_init(void)
146{ 155{
@@ -155,6 +164,10 @@ static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr)
155static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr) 164static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr)
156{ 165{
157} 166}
167static inline void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
168 struct brcmf_fws_stats *stats)
169{
170}
158#endif 171#endif
159 172
160#endif /* _BRCMF_DBG_H_ */ 173#endif /* _BRCMF_DBG_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index 9d0faebda8af..172d39cdb4ea 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -30,6 +30,7 @@
30#include "p2p.h" 30#include "p2p.h"
31#include "wl_cfg80211.h" 31#include "wl_cfg80211.h"
32#include "fwil.h" 32#include "fwil.h"
33#include "fwsignal.h"
33 34
34MODULE_AUTHOR("Broadcom Corporation"); 35MODULE_AUTHOR("Broadcom Corporation");
35MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); 36MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
@@ -283,7 +284,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
283 skb_unlink(skb, skb_list); 284 skb_unlink(skb, skb_list);
284 285
285 /* process and remove protocol-specific header */ 286 /* process and remove protocol-specific header */
286 ret = brcmf_proto_hdrpull(drvr, &ifidx, skb); 287 ret = brcmf_proto_hdrpull(drvr, drvr->fw_signals, &ifidx, skb);
287 ifp = drvr->iflist[ifidx]; 288 ifp = drvr->iflist[ifidx];
288 289
289 if (ret || !ifp || !ifp->ndev) { 290 if (ret || !ifp || !ifp->ndev) {
@@ -357,20 +358,23 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
357 struct brcmf_bus *bus_if = dev_get_drvdata(dev); 358 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
358 struct brcmf_pub *drvr = bus_if->drvr; 359 struct brcmf_pub *drvr = bus_if->drvr;
359 struct brcmf_if *ifp; 360 struct brcmf_if *ifp;
361 int res;
360 362
361 brcmf_proto_hdrpull(drvr, &ifidx, txp); 363 res = brcmf_proto_hdrpull(drvr, false, &ifidx, txp);
362 364
363 ifp = drvr->iflist[ifidx]; 365 ifp = drvr->iflist[ifidx];
364 if (!ifp) 366 if (!ifp)
365 return; 367 return;
366 368
367 eh = (struct ethhdr *)(txp->data); 369 if (res == 0) {
368 type = ntohs(eh->h_proto); 370 eh = (struct ethhdr *)(txp->data);
371 type = ntohs(eh->h_proto);
369 372
370 if (type == ETH_P_PAE) { 373 if (type == ETH_P_PAE) {
371 atomic_dec(&ifp->pend_8021x_cnt); 374 atomic_dec(&ifp->pend_8021x_cnt);
372 if (waitqueue_active(&ifp->pend_8021x_wait)) 375 if (waitqueue_active(&ifp->pend_8021x_wait))
373 wake_up(&ifp->pend_8021x_wait); 376 wake_up(&ifp->pend_8021x_wait);
377 }
374 } 378 }
375 if (!success) 379 if (!success)
376 ifp->stats.tx_errors++; 380 ifp->stats.tx_errors++;
@@ -873,6 +877,9 @@ int brcmf_bus_start(struct device *dev)
873 if (ret < 0) 877 if (ret < 0)
874 goto fail; 878 goto fail;
875 879
880 drvr->fw_signals = true;
881 (void)brcmf_fws_init(drvr);
882
876 drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev); 883 drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev);
877 if (drvr->config == NULL) { 884 if (drvr->config == NULL) {
878 ret = -ENOMEM; 885 ret = -ENOMEM;
@@ -889,6 +896,8 @@ fail:
889 brcmf_err("failed: %d\n", ret); 896 brcmf_err("failed: %d\n", ret);
890 if (drvr->config) 897 if (drvr->config)
891 brcmf_cfg80211_detach(drvr->config); 898 brcmf_cfg80211_detach(drvr->config);
899 if (drvr->fws)
900 brcmf_fws_deinit(drvr);
892 free_netdev(ifp->ndev); 901 free_netdev(ifp->ndev);
893 drvr->iflist[0] = NULL; 902 drvr->iflist[0] = NULL;
894 if (p2p_ifp) { 903 if (p2p_ifp) {
@@ -952,6 +961,9 @@ void brcmf_detach(struct device *dev)
952 if (drvr->prot) 961 if (drvr->prot)
953 brcmf_proto_detach(drvr); 962 brcmf_proto_detach(drvr);
954 963
964 if (drvr->fws)
965 brcmf_fws_deinit(drvr);
966
955 brcmf_debugfs_detach(drvr); 967 brcmf_debugfs_detach(drvr);
956 bus_if->drvr = NULL; 968 bus_if->drvr = NULL;
957 kfree(drvr); 969 kfree(drvr);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index 4469321c0eb3..bf6ab41b7b1e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -1546,7 +1546,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
1546 struct sk_buff_head pktlist; /* needed for bus interface */ 1546 struct sk_buff_head pktlist; /* needed for bus interface */
1547 u16 pad; /* Number of pad bytes to read */ 1547 u16 pad; /* Number of pad bytes to read */
1548 uint rxleft = 0; /* Remaining number of frames allowed */ 1548 uint rxleft = 0; /* Remaining number of frames allowed */
1549 int sdret; /* Return code from calls */ 1549 int ret; /* Return code from calls */
1550 uint rxcount = 0; /* Total frames read */ 1550 uint rxcount = 0; /* Total frames read */
1551 struct brcmf_sdio_read *rd = &bus->cur_read, rd_new; 1551 struct brcmf_sdio_read *rd = &bus->cur_read, rd_new;
1552 u8 head_read = 0; 1552 u8 head_read = 0;
@@ -1577,15 +1577,15 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
1577 /* read header first for unknow frame length */ 1577 /* read header first for unknow frame length */
1578 sdio_claim_host(bus->sdiodev->func[1]); 1578 sdio_claim_host(bus->sdiodev->func[1]);
1579 if (!rd->len) { 1579 if (!rd->len) {
1580 sdret = brcmf_sdcard_recv_buf(bus->sdiodev, 1580 ret = brcmf_sdcard_recv_buf(bus->sdiodev,
1581 bus->sdiodev->sbwad, 1581 bus->sdiodev->sbwad,
1582 SDIO_FUNC_2, F2SYNC, 1582 SDIO_FUNC_2, F2SYNC,
1583 bus->rxhdr, 1583 bus->rxhdr,
1584 BRCMF_FIRSTREAD); 1584 BRCMF_FIRSTREAD);
1585 bus->sdcnt.f2rxhdrs++; 1585 bus->sdcnt.f2rxhdrs++;
1586 if (sdret < 0) { 1586 if (ret < 0) {
1587 brcmf_err("RXHEADER FAILED: %d\n", 1587 brcmf_err("RXHEADER FAILED: %d\n",
1588 sdret); 1588 ret);
1589 bus->sdcnt.rx_hdrfail++; 1589 bus->sdcnt.rx_hdrfail++;
1590 brcmf_sdbrcm_rxfail(bus, true, true); 1590 brcmf_sdbrcm_rxfail(bus, true, true);
1591 sdio_release_host(bus->sdiodev->func[1]); 1591 sdio_release_host(bus->sdiodev->func[1]);
@@ -1637,14 +1637,14 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
1637 skb_pull(pkt, head_read); 1637 skb_pull(pkt, head_read);
1638 pkt_align(pkt, rd->len_left, BRCMF_SDALIGN); 1638 pkt_align(pkt, rd->len_left, BRCMF_SDALIGN);
1639 1639
1640 sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, 1640 ret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
1641 SDIO_FUNC_2, F2SYNC, pkt); 1641 SDIO_FUNC_2, F2SYNC, pkt);
1642 bus->sdcnt.f2rxdata++; 1642 bus->sdcnt.f2rxdata++;
1643 sdio_release_host(bus->sdiodev->func[1]); 1643 sdio_release_host(bus->sdiodev->func[1]);
1644 1644
1645 if (sdret < 0) { 1645 if (ret < 0) {
1646 brcmf_err("read %d bytes from channel %d failed: %d\n", 1646 brcmf_err("read %d bytes from channel %d failed: %d\n",
1647 rd->len, rd->channel, sdret); 1647 rd->len, rd->channel, ret);
1648 brcmu_pkt_buf_free_skb(pkt); 1648 brcmu_pkt_buf_free_skb(pkt);
1649 sdio_claim_host(bus->sdiodev->func[1]); 1649 sdio_claim_host(bus->sdiodev->func[1]);
1650 brcmf_sdbrcm_rxfail(bus, true, 1650 brcmf_sdbrcm_rxfail(bus, true,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
new file mode 100644
index 000000000000..071d55f9cd4d
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
@@ -0,0 +1,382 @@
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(&timestamp, &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}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
new file mode 100644
index 000000000000..e728eea72bb4
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
@@ -0,0 +1,25 @@
1/*
2 * Copyright (c) 2012 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
17
18#ifndef FWSIGNAL_H_
19#define FWSIGNAL_H_
20
21int brcmf_fws_init(struct brcmf_pub *drvr);
22void brcmf_fws_deinit(struct brcmf_pub *drvr);
23int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
24 struct sk_buff *skb);
25#endif /* FWSIGNAL_H_ */