Skip to content

Commit

Permalink
brcmfmac: add support for TLV based firmware signalling
Browse files Browse the repository at this point in the history
The firmware and host can exchange signals which are carried within
the data packets. These are TLV based signals that are inserted
before the actual data, ie. ethernet frame.

This commit adds the new source module for this feature and enables
RSSI signals from firmware.

Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Arend van Spriel authored and John W. Linville committed Mar 6, 2013
1 parent edee166 commit 349e710
Show file tree
Hide file tree
Showing 9 changed files with 501 additions and 18 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ brcmfmac-objs += \
wl_cfg80211.o \
fwil.o \
fweh.o \
fwsignal.o \
p2p.o \
dhd_cdc.o \
dhd_common.o \
Expand Down
7 changes: 6 additions & 1 deletion drivers/net/wireless/brcm80211/brcmfmac/dhd.h
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ struct brcmf_dcmd {
/* Forward decls for struct brcmf_pub (see below) */
struct brcmf_proto; /* device communication protocol info */
struct brcmf_cfg80211_dev; /* cfg80211 device info */
struct brcmf_fws_info; /* firmware signalling info */

/* Common structure for module and instance linkage */
struct brcmf_pub {
Expand All @@ -527,6 +528,10 @@ struct brcmf_pub {
unsigned char proto_buf[BRCMF_DCMD_MAXLEN];

struct brcmf_fweh_info fweh;

bool fw_signals;
struct brcmf_fws_info *fws;
spinlock_t fws_spinlock;
#ifdef DEBUG
struct dentry *dbgfs_dir;
#endif
Expand Down Expand Up @@ -582,7 +587,7 @@ extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
void *buf, uint len);

/* Remove any protocol-specific data header. */
extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
struct sk_buff *rxp);

extern int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
Expand Down
8 changes: 6 additions & 2 deletions drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "dhd.h"
#include "dhd_proto.h"
#include "dhd_bus.h"
#include "fwsignal.h"
#include "dhd_dbg.h"

struct brcmf_proto_cdc_dcmd {
Expand Down Expand Up @@ -294,7 +295,7 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
BDC_SET_IF_IDX(h, ifidx);
}

int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
struct sk_buff *pktbuf)
{
struct brcmf_proto_bdc_header *h;
Expand Down Expand Up @@ -341,7 +342,10 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
pktbuf->priority = h->priority & BDC_PRIORITY_MASK;

skb_pull(pktbuf, BDC_HEADER_LEN);
skb_pull(pktbuf, h->data_offset << 2);
if (do_fws)
brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf);
else
skb_pull(pktbuf, h->data_offset << 2);

if (pktbuf->len == 0)
return -ENODATA;
Expand Down
41 changes: 41 additions & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,44 @@ void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
debugfs_create_file("counters", S_IRUGO, dentry,
sdcnt, &brcmf_debugfs_sdio_counter_ops);
}

static
ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
size_t count, loff_t *ppos)
{
struct brcmf_fws_stats *fwstats = f->private_data;
char buf[100];
int res;

/* only allow read from start */
if (*ppos > 0)
return 0;

res = scnprintf(buf, sizeof(buf),
"header_pulls: %u\n"
"header_only_pkt: %u\n"
"tlv_parse_failed: %u\n"
"tlv_invalid_type: %u\n",
fwstats->header_pulls,
fwstats->header_only_pkt,
fwstats->tlv_parse_failed,
fwstats->tlv_invalid_type);

return simple_read_from_buffer(data, count, ppos, buf, res);
}

static const struct file_operations brcmf_debugfs_fws_stats_ops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = brcmf_debugfs_fws_stats_read
};

void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
struct brcmf_fws_stats *stats)
{
struct dentry *dentry = drvr->dbgfs_dir;

if (!IS_ERR_OR_NULL(dentry))
debugfs_create_file("fws_stats", S_IRUGO, dentry,
stats, &brcmf_debugfs_fws_stats_ops);
}
13 changes: 13 additions & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ struct brcmf_sdio_count {
ulong rx_readahead_cnt; /* packets where header read-ahead was used */
};

struct brcmf_fws_stats {
u32 tlv_parse_failed;
u32 tlv_invalid_type;
u32 header_only_pkt;
u32 header_pulls;
};

struct brcmf_pub;
#ifdef DEBUG
void brcmf_debugfs_init(void);
Expand All @@ -141,6 +148,8 @@ void brcmf_debugfs_detach(struct brcmf_pub *drvr);
struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
struct brcmf_sdio_count *sdcnt);
void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
struct brcmf_fws_stats *stats);
#else
static inline void brcmf_debugfs_init(void)
{
Expand All @@ -155,6 +164,10 @@ static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr)
static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr)
{
}
static inline void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
struct brcmf_fws_stats *stats)
{
}
#endif

#endif /* _BRCMF_DBG_H_ */
28 changes: 20 additions & 8 deletions drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "p2p.h"
#include "wl_cfg80211.h"
#include "fwil.h"
#include "fwsignal.h"

MODULE_AUTHOR("Broadcom Corporation");
MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
Expand Down Expand Up @@ -283,7 +284,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
skb_unlink(skb, skb_list);

/* process and remove protocol-specific header */
ret = brcmf_proto_hdrpull(drvr, &ifidx, skb);
ret = brcmf_proto_hdrpull(drvr, drvr->fw_signals, &ifidx, skb);
ifp = drvr->iflist[ifidx];

if (ret || !ifp || !ifp->ndev) {
Expand Down Expand Up @@ -357,20 +358,23 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
struct brcmf_if *ifp;
int res;

brcmf_proto_hdrpull(drvr, &ifidx, txp);
res = brcmf_proto_hdrpull(drvr, false, &ifidx, txp);

ifp = drvr->iflist[ifidx];
if (!ifp)
return;

eh = (struct ethhdr *)(txp->data);
type = ntohs(eh->h_proto);
if (res == 0) {
eh = (struct ethhdr *)(txp->data);
type = ntohs(eh->h_proto);

if (type == ETH_P_PAE) {
atomic_dec(&ifp->pend_8021x_cnt);
if (waitqueue_active(&ifp->pend_8021x_wait))
wake_up(&ifp->pend_8021x_wait);
if (type == ETH_P_PAE) {
atomic_dec(&ifp->pend_8021x_cnt);
if (waitqueue_active(&ifp->pend_8021x_wait))
wake_up(&ifp->pend_8021x_wait);
}
}
if (!success)
ifp->stats.tx_errors++;
Expand Down Expand Up @@ -873,6 +877,9 @@ int brcmf_bus_start(struct device *dev)
if (ret < 0)
goto fail;

drvr->fw_signals = true;
(void)brcmf_fws_init(drvr);

drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev);
if (drvr->config == NULL) {
ret = -ENOMEM;
Expand All @@ -889,6 +896,8 @@ int brcmf_bus_start(struct device *dev)
brcmf_err("failed: %d\n", ret);
if (drvr->config)
brcmf_cfg80211_detach(drvr->config);
if (drvr->fws)
brcmf_fws_deinit(drvr);
free_netdev(ifp->ndev);
drvr->iflist[0] = NULL;
if (p2p_ifp) {
Expand Down Expand Up @@ -952,6 +961,9 @@ void brcmf_detach(struct device *dev)
if (drvr->prot)
brcmf_proto_detach(drvr);

if (drvr->fws)
brcmf_fws_deinit(drvr);

brcmf_debugfs_detach(drvr);
bus_if->drvr = NULL;
kfree(drvr);
Expand Down
14 changes: 7 additions & 7 deletions drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
struct sk_buff_head pktlist; /* needed for bus interface */
u16 pad; /* Number of pad bytes to read */
uint rxleft = 0; /* Remaining number of frames allowed */
int sdret; /* Return code from calls */
int ret; /* Return code from calls */
uint rxcount = 0; /* Total frames read */
struct brcmf_sdio_read *rd = &bus->cur_read, rd_new;
u8 head_read = 0;
Expand Down Expand Up @@ -1577,15 +1577,15 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
/* read header first for unknow frame length */
sdio_claim_host(bus->sdiodev->func[1]);
if (!rd->len) {
sdret = brcmf_sdcard_recv_buf(bus->sdiodev,
ret = brcmf_sdcard_recv_buf(bus->sdiodev,
bus->sdiodev->sbwad,
SDIO_FUNC_2, F2SYNC,
bus->rxhdr,
BRCMF_FIRSTREAD);
bus->sdcnt.f2rxhdrs++;
if (sdret < 0) {
if (ret < 0) {
brcmf_err("RXHEADER FAILED: %d\n",
sdret);
ret);
bus->sdcnt.rx_hdrfail++;
brcmf_sdbrcm_rxfail(bus, true, true);
sdio_release_host(bus->sdiodev->func[1]);
Expand Down Expand Up @@ -1637,14 +1637,14 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
skb_pull(pkt, head_read);
pkt_align(pkt, rd->len_left, BRCMF_SDALIGN);

sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
ret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
SDIO_FUNC_2, F2SYNC, pkt);
bus->sdcnt.f2rxdata++;
sdio_release_host(bus->sdiodev->func[1]);

if (sdret < 0) {
if (ret < 0) {
brcmf_err("read %d bytes from channel %d failed: %d\n",
rd->len, rd->channel, sdret);
rd->len, rd->channel, ret);
brcmu_pkt_buf_free_skb(pkt);
sdio_claim_host(bus->sdiodev->func[1]);
brcmf_sdbrcm_rxfail(bus, true,
Expand Down
Loading

0 comments on commit 349e710

Please sign in to comment.