diff options
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 7 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 41 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 13 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 28 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 14 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 382 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h | 25 |
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) */ |
502 | struct brcmf_proto; /* device communication protocol info */ | 502 | struct brcmf_proto; /* device communication protocol info */ |
503 | struct brcmf_cfg80211_dev; /* cfg80211 device info */ | 503 | struct brcmf_cfg80211_dev; /* cfg80211 device info */ |
504 | struct brcmf_fws_info; /* firmware signalling info */ | ||
504 | 505 | ||
505 | /* Common structure for module and instance linkage */ | 506 | /* Common structure for module and instance linkage */ |
506 | struct brcmf_pub { | 507 | struct 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. */ |
585 | extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx, | 590 | extern 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 | ||
588 | extern int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); | 593 | extern 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 | ||
33 | struct brcmf_proto_cdc_dcmd { | 34 | struct 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 | ||
297 | int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx, | 298 | int 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 | |||
128 | static | ||
129 | ssize_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 | |||
153 | static 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 | |||
159 | void 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 | ||
135 | struct brcmf_fws_stats { | ||
136 | u32 tlv_parse_failed; | ||
137 | u32 tlv_invalid_type; | ||
138 | u32 header_only_pkt; | ||
139 | u32 header_pulls; | ||
140 | }; | ||
141 | |||
135 | struct brcmf_pub; | 142 | struct brcmf_pub; |
136 | #ifdef DEBUG | 143 | #ifdef DEBUG |
137 | void brcmf_debugfs_init(void); | 144 | void brcmf_debugfs_init(void); |
@@ -141,6 +148,8 @@ void brcmf_debugfs_detach(struct brcmf_pub *drvr); | |||
141 | struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); | 148 | struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); |
142 | void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, | 149 | void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, |
143 | struct brcmf_sdio_count *sdcnt); | 150 | struct brcmf_sdio_count *sdcnt); |
151 | void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr, | ||
152 | struct brcmf_fws_stats *stats); | ||
144 | #else | 153 | #else |
145 | static inline void brcmf_debugfs_init(void) | 154 | static inline void brcmf_debugfs_init(void) |
146 | { | 155 | { |
@@ -155,6 +164,10 @@ static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr) | |||
155 | static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr) | 164 | static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr) |
156 | { | 165 | { |
157 | } | 166 | } |
167 | static 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 | ||
34 | MODULE_AUTHOR("Broadcom Corporation"); | 35 | MODULE_AUTHOR("Broadcom Corporation"); |
35 | MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); | 36 | MODULE_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, | ||
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 | /** | ||
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, | ||
81 | enum 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 }, | ||
92 | static 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 | |||
100 | static 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 | ||
111 | static 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 | |||
180 | struct brcmf_fws_info { | ||
181 | struct brcmf_pub *drvr; | ||
182 | struct brcmf_fws_stats stats; | ||
183 | }; | ||
184 | |||
185 | static 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 | |||
191 | static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) | ||
192 | { | ||
193 | __le32 timestamp; | ||
194 | |||
195 | memcpy(×tamp, &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) \ | ||
205 | do { \ | ||
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 | |||
216 | int 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 | |||
249 | fail: | ||
250 | /* disable flow control entirely */ | ||
251 | drvr->fw_signals = false; | ||
252 | brcmf_fws_deinit(drvr); | ||
253 | return rc; | ||
254 | } | ||
255 | |||
256 | void brcmf_fws_deinit(struct brcmf_pub *drvr) | ||
257 | { | ||
258 | /* free top structure */ | ||
259 | kfree(drvr->fws); | ||
260 | drvr->fws = NULL; | ||
261 | } | ||
262 | |||
263 | int 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 | |||
21 | int brcmf_fws_init(struct brcmf_pub *drvr); | ||
22 | void brcmf_fws_deinit(struct brcmf_pub *drvr); | ||
23 | int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, | ||
24 | struct sk_buff *skb); | ||
25 | #endif /* FWSIGNAL_H_ */ | ||