diff options
author | Arend van Spriel <arend@broadcom.com> | 2013-04-03 06:40:40 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-04-03 15:07:06 -0400 |
commit | 3edc1cff02a40a76ad6a5e2b9cb00a29584f33ad (patch) | |
tree | 77c0d0825f08cd12326f92c92a0e272459d4fd06 /drivers | |
parent | c7f34a69a2e32b139a6b66c8599252c46f37abba (diff) |
brcmfmac: enable tx status signalling
Enabling the tx status signalling, which requires packet tagging
before sending to the firmware and handling the tx status signal.
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Piotr Haber <phaber@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 30 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 18 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 485 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h | 1 |
5 files changed, 455 insertions, 87 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index f0949b1e3073..757701d57e5a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | |||
@@ -138,16 +138,34 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, | |||
138 | return 0; | 138 | return 0; |
139 | 139 | ||
140 | res = scnprintf(buf, sizeof(buf), | 140 | res = scnprintf(buf, sizeof(buf), |
141 | "header_pulls: %u\n" | 141 | "header_pulls: %u\n" |
142 | "header_only_pkt: %u\n" | 142 | "header_only_pkt: %u\n" |
143 | "tlv_parse_failed: %u\n" | 143 | "tlv_parse_failed: %u\n" |
144 | "tlv_invalid_type: %u\n" | 144 | "tlv_invalid_type: %u\n" |
145 | "mac_update_fails: %u\n", | 145 | "mac_update_fails: %u\n" |
146 | "pkt2bus: %u\n" | ||
147 | "generic_error: %u\n" | ||
148 | "txs_indicate: %u\n" | ||
149 | "txs_discard: %u\n" | ||
150 | "txs_suppr_core: %u\n" | ||
151 | "txs_suppr_ps: %u\n" | ||
152 | "txs_tossed: %u\n" | ||
153 | "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", | ||
146 | fwstats->header_pulls, | 154 | fwstats->header_pulls, |
147 | fwstats->header_only_pkt, | 155 | fwstats->header_only_pkt, |
148 | fwstats->tlv_parse_failed, | 156 | fwstats->tlv_parse_failed, |
149 | fwstats->tlv_invalid_type, | 157 | fwstats->tlv_invalid_type, |
150 | fwstats->mac_update_failed); | 158 | fwstats->mac_update_failed, |
159 | fwstats->pkt2bus, | ||
160 | fwstats->generic_error, | ||
161 | fwstats->txs_indicate, | ||
162 | fwstats->txs_discard, | ||
163 | fwstats->txs_supp_core, | ||
164 | fwstats->txs_supp_ps, | ||
165 | fwstats->txs_tossed, | ||
166 | fwstats->send_pkts[0], fwstats->send_pkts[1], | ||
167 | fwstats->send_pkts[2], fwstats->send_pkts[3], | ||
168 | fwstats->send_pkts[4]); | ||
151 | 169 | ||
152 | return simple_read_from_buffer(data, count, ppos, buf, res); | 170 | return simple_read_from_buffer(data, count, ppos, buf, res); |
153 | } | 171 | } |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 371ae5b546af..ef93fe449d1c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | |||
@@ -137,7 +137,15 @@ struct brcmf_fws_stats { | |||
137 | u32 tlv_invalid_type; | 137 | u32 tlv_invalid_type; |
138 | u32 header_only_pkt; | 138 | u32 header_only_pkt; |
139 | u32 header_pulls; | 139 | u32 header_pulls; |
140 | u32 pkt2bus; | ||
141 | u32 send_pkts[5]; | ||
142 | u32 generic_error; | ||
140 | u32 mac_update_failed; | 143 | u32 mac_update_failed; |
144 | u32 txs_indicate; | ||
145 | u32 txs_discard; | ||
146 | u32 txs_supp_core; | ||
147 | u32 txs_supp_ps; | ||
148 | u32 txs_tossed; | ||
141 | }; | 149 | }; |
142 | 150 | ||
143 | struct brcmf_pub; | 151 | struct brcmf_pub; |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 05c8840392e5..0299ab6731b2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | |||
@@ -223,18 +223,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, | |||
223 | goto done; | 223 | goto done; |
224 | } | 224 | } |
225 | 225 | ||
226 | /* handle ethernet header */ | 226 | ret = brcmf_fws_process_skb(ifp, skb); |
227 | eh = (struct ethhdr *)(skb->data); | ||
228 | if (is_multicast_ether_addr(eh->h_dest)) | ||
229 | drvr->tx_multicast++; | ||
230 | if (ntohs(eh->h_proto) == ETH_P_PAE) | ||
231 | atomic_inc(&ifp->pend_8021x_cnt); | ||
232 | |||
233 | /* If the protocol uses a data header, apply it */ | ||
234 | brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb); | ||
235 | |||
236 | /* Use bus module to send data frame */ | ||
237 | ret = brcmf_bus_txdata(drvr->bus_if, skb); | ||
238 | 227 | ||
239 | done: | 228 | done: |
240 | if (ret) { | 229 | if (ret) { |
@@ -376,7 +365,7 @@ void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, | |||
376 | 365 | ||
377 | ifp = drvr->iflist[ifidx]; | 366 | ifp = drvr->iflist[ifidx]; |
378 | if (!ifp) | 367 | if (!ifp) |
379 | return; | 368 | goto done; |
380 | 369 | ||
381 | if (res == 0) { | 370 | if (res == 0) { |
382 | eh = (struct ethhdr *)(txp->data); | 371 | eh = (struct ethhdr *)(txp->data); |
@@ -390,6 +379,8 @@ void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, | |||
390 | } | 379 | } |
391 | if (!success) | 380 | if (!success) |
392 | ifp->stats.tx_errors++; | 381 | ifp->stats.tx_errors++; |
382 | done: | ||
383 | brcmu_pkt_buf_free_skb(txp); | ||
393 | } | 384 | } |
394 | 385 | ||
395 | void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) | 386 | void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) |
@@ -402,7 +393,6 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) | |||
402 | return; | 393 | return; |
403 | 394 | ||
404 | brcmf_txfinalize(drvr, txp, success); | 395 | brcmf_txfinalize(drvr, txp, success); |
405 | brcmu_pkt_buf_free_skb(txp); | ||
406 | } | 396 | } |
407 | 397 | ||
408 | static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) | 398 | static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index cb1414d93a67..9389bf7b7696 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | |||
@@ -19,12 +19,15 @@ | |||
19 | #include <linux/spinlock.h> | 19 | #include <linux/spinlock.h> |
20 | #include <linux/skbuff.h> | 20 | #include <linux/skbuff.h> |
21 | #include <linux/netdevice.h> | 21 | #include <linux/netdevice.h> |
22 | #include <linux/etherdevice.h> | ||
22 | #include <linux/err.h> | 23 | #include <linux/err.h> |
23 | #include <uapi/linux/nl80211.h> | 24 | #include <uapi/linux/nl80211.h> |
25 | #include <net/cfg80211.h> | ||
24 | 26 | ||
25 | #include <brcmu_utils.h> | 27 | #include <brcmu_utils.h> |
26 | #include <brcmu_wifi.h> | 28 | #include <brcmu_wifi.h> |
27 | #include "dhd.h" | 29 | #include "dhd.h" |
30 | #include "dhd_proto.h" | ||
28 | #include "dhd_dbg.h" | 31 | #include "dhd_dbg.h" |
29 | #include "dhd_bus.h" | 32 | #include "dhd_bus.h" |
30 | #include "fwil.h" | 33 | #include "fwil.h" |
@@ -64,7 +67,7 @@ | |||
64 | BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \ | 67 | BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \ |
65 | BRCMF_FWS_TLV_DEF(FILLER, 255, 0) | 68 | BRCMF_FWS_TLV_DEF(FILLER, 255, 0) |
66 | 69 | ||
67 | /** | 70 | /* |
68 | * enum brcmf_fws_tlv_type - definition of tlv identifiers. | 71 | * enum brcmf_fws_tlv_type - definition of tlv identifiers. |
69 | */ | 72 | */ |
70 | #define BRCMF_FWS_TLV_DEF(name, id, len) \ | 73 | #define BRCMF_FWS_TLV_DEF(name, id, len) \ |
@@ -75,8 +78,18 @@ enum brcmf_fws_tlv_type { | |||
75 | }; | 78 | }; |
76 | #undef BRCMF_FWS_TLV_DEF | 79 | #undef BRCMF_FWS_TLV_DEF |
77 | 80 | ||
81 | /* | ||
82 | * enum brcmf_fws_tlv_len - definition of tlv lengths. | ||
83 | */ | ||
84 | #define BRCMF_FWS_TLV_DEF(name, id, len) \ | ||
85 | BRCMF_FWS_TYPE_ ## name ## _LEN = (len), | ||
86 | enum brcmf_fws_tlv_len { | ||
87 | BRCMF_FWS_TLV_DEFLIST | ||
88 | }; | ||
89 | #undef BRCMF_FWS_TLV_DEF | ||
90 | |||
78 | #ifdef DEBUG | 91 | #ifdef DEBUG |
79 | /** | 92 | /* |
80 | * brcmf_fws_tlv_names - array of tlv names. | 93 | * brcmf_fws_tlv_names - array of tlv names. |
81 | */ | 94 | */ |
82 | #define BRCMF_FWS_TLV_DEF(name, id, len) \ | 95 | #define BRCMF_FWS_TLV_DEF(name, id, len) \ |
@@ -106,7 +119,7 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) | |||
106 | } | 119 | } |
107 | #endif /* DEBUG */ | 120 | #endif /* DEBUG */ |
108 | 121 | ||
109 | /** | 122 | /* |
110 | * flags used to enable tlv signalling from firmware. | 123 | * flags used to enable tlv signalling from firmware. |
111 | */ | 124 | */ |
112 | #define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001 | 125 | #define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001 |
@@ -117,11 +130,6 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) | |||
117 | #define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 | 130 | #define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 |
118 | #define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040 | 131 | #define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040 |
119 | 132 | ||
120 | #define BRCMF_FWS_HANGER_MAXITEMS 1024 | ||
121 | #define BRCMF_FWS_HANGER_ITEM_STATE_FREE 1 | ||
122 | #define BRCMF_FWS_HANGER_ITEM_STATE_INUSE 2 | ||
123 | #define BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED 3 | ||
124 | |||
125 | #define BRCMF_FWS_STATE_OPEN 1 | 133 | #define BRCMF_FWS_STATE_OPEN 1 |
126 | #define BRCMF_FWS_STATE_CLOSE 2 | 134 | #define BRCMF_FWS_STATE_CLOSE 2 |
127 | 135 | ||
@@ -135,23 +143,27 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) | |||
135 | #define BRCMF_FWS_PSQ_PREC_COUNT ((NL80211_NUM_ACS + 1) * 2) | 143 | #define BRCMF_FWS_PSQ_PREC_COUNT ((NL80211_NUM_ACS + 1) * 2) |
136 | #define BRCMF_FWS_PSQ_LEN 256 | 144 | #define BRCMF_FWS_PSQ_LEN 256 |
137 | 145 | ||
146 | #define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01 | ||
147 | #define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02 | ||
148 | |||
138 | /** | 149 | /** |
139 | * enum brcmf_fws_skb_state - indicates processing state of skb. | 150 | * enum brcmf_fws_skb_state - indicates processing state of skb. |
151 | * | ||
152 | * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver. | ||
153 | * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue. | ||
154 | * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware. | ||
140 | */ | 155 | */ |
141 | enum brcmf_fws_skb_state { | 156 | enum brcmf_fws_skb_state { |
142 | WLFC_PKTTYPE_NEW, | 157 | BRCMF_FWS_SKBSTATE_NEW, |
143 | WLFC_PKTTYPE_DELAYED, | 158 | BRCMF_FWS_SKBSTATE_DELAYED, |
144 | WLFC_PKTTYPE_SUPPRESSED, | 159 | BRCMF_FWS_SKBSTATE_SUPPRESSED |
145 | WLFC_PKTTYPE_MAX | ||
146 | }; | 160 | }; |
147 | 161 | ||
148 | /** | 162 | /** |
149 | * struct brcmf_skbuff_cb - control buffer associated with skbuff. | 163 | * struct brcmf_skbuff_cb - control buffer associated with skbuff. |
150 | * | 164 | * |
151 | * @if_flags: holds interface index and packet related flags. | 165 | * @if_flags: holds interface index and packet related flags. |
152 | * @da: destination MAC address extracted from skbuff once. | ||
153 | * @htod: host to device packet identifier (used in PKTTAG tlv). | 166 | * @htod: host to device packet identifier (used in PKTTAG tlv). |
154 | * @needs_hdr: the packet does not yet have a BDC header. | ||
155 | * @state: transmit state of the packet. | 167 | * @state: transmit state of the packet. |
156 | * @mac: descriptor related to destination for this packet. | 168 | * @mac: descriptor related to destination for this packet. |
157 | * | 169 | * |
@@ -160,19 +172,17 @@ enum brcmf_fws_skb_state { | |||
160 | */ | 172 | */ |
161 | struct brcmf_skbuff_cb { | 173 | struct brcmf_skbuff_cb { |
162 | u16 if_flags; | 174 | u16 if_flags; |
163 | u8 da[ETH_ALEN]; | ||
164 | u32 htod; | 175 | u32 htod; |
165 | u8 needs_hdr; | ||
166 | enum brcmf_fws_skb_state state; | 176 | enum brcmf_fws_skb_state state; |
167 | struct brcmf_fws_mac_descriptor *mac; | 177 | struct brcmf_fws_mac_descriptor *mac; |
168 | }; | 178 | }; |
169 | 179 | ||
170 | /** | 180 | /* |
171 | * macro casting skbuff control buffer to struct brcmf_skbuff_cb. | 181 | * macro casting skbuff control buffer to struct brcmf_skbuff_cb. |
172 | */ | 182 | */ |
173 | #define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb)) | 183 | #define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb)) |
174 | 184 | ||
175 | /** | 185 | /* |
176 | * sk_buff control if flags | 186 | * sk_buff control if flags |
177 | * | 187 | * |
178 | * b[11] - packet sent upon firmware request. | 188 | * b[11] - packet sent upon firmware request. |
@@ -207,7 +217,7 @@ struct brcmf_skbuff_cb { | |||
207 | BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \ | 217 | BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \ |
208 | BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT) | 218 | BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT) |
209 | 219 | ||
210 | /** | 220 | /* |
211 | * sk_buff control packet identifier | 221 | * sk_buff control packet identifier |
212 | * | 222 | * |
213 | * 32-bit packet identifier used in PKTTAG tlv from host to dongle. | 223 | * 32-bit packet identifier used in PKTTAG tlv from host to dongle. |
@@ -242,6 +252,61 @@ struct brcmf_skbuff_cb { | |||
242 | BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \ | 252 | BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \ |
243 | BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT) | 253 | BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT) |
244 | 254 | ||
255 | #define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000 | ||
256 | #define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31 | ||
257 | #define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000 | ||
258 | #define BRCMF_FWS_TXSTAT_FLAGS_SHIFT 27 | ||
259 | #define BRCMF_FWS_TXSTAT_FIFO_MASK 0x07000000 | ||
260 | #define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24 | ||
261 | #define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00 | ||
262 | #define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8 | ||
263 | #define BRCMF_FWS_TXSTAT_PKTID_MASK 0x00FFFFFF | ||
264 | #define BRCMF_FWS_TXSTAT_PKTID_SHIFT 0 | ||
265 | |||
266 | #define brcmf_txstatus_get_field(txs, field) \ | ||
267 | brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \ | ||
268 | BRCMF_FWS_TXSTAT_ ## field ## _SHIFT) | ||
269 | |||
270 | /** | ||
271 | * enum brcmf_fws_fifo - fifo indices used by dongle firmware. | ||
272 | * | ||
273 | * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic. | ||
274 | * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic. | ||
275 | * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic. | ||
276 | * @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic. | ||
277 | * @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only). | ||
278 | * @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only). | ||
279 | * @BRCMF_FWS_FIFO_COUNT: number of fifos. | ||
280 | */ | ||
281 | enum brcmf_fws_fifo { | ||
282 | BRCMF_FWS_FIFO_AC_BK, | ||
283 | BRCMF_FWS_FIFO_AC_BE, | ||
284 | BRCMF_FWS_FIFO_AC_VI, | ||
285 | BRCMF_FWS_FIFO_AC_VO, | ||
286 | BRCMF_FWS_FIFO_BCMC, | ||
287 | BRCMF_FWS_FIFO_ATIM, | ||
288 | BRCMF_FWS_FIFO_COUNT | ||
289 | }; | ||
290 | |||
291 | /** | ||
292 | * enum brcmf_fws_txstatus - txstatus flag values. | ||
293 | * | ||
294 | * @BRCMF_FWS_TXSTATUS_DISCARD: | ||
295 | * host is free to discard the packet. | ||
296 | * @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS: | ||
297 | * 802.11 core suppressed the packet. | ||
298 | * @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS: | ||
299 | * firmware suppress the packet as device is already in PS mode. | ||
300 | * @BRCMF_FWS_TXSTATUS_FW_TOSSED: | ||
301 | * firmware tossed the packet. | ||
302 | */ | ||
303 | enum brcmf_fws_txstatus { | ||
304 | BRCMF_FWS_TXSTATUS_DISCARD, | ||
305 | BRCMF_FWS_TXSTATUS_CORE_SUPPRESS, | ||
306 | BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS, | ||
307 | BRCMF_FWS_TXSTATUS_FW_TOSSED | ||
308 | }; | ||
309 | |||
245 | enum brcmf_fws_fcmode { | 310 | enum brcmf_fws_fcmode { |
246 | BRCMF_FWS_FCMODE_NONE, | 311 | BRCMF_FWS_FCMODE_NONE, |
247 | BRCMF_FWS_FCMODE_IMPLIED_CREDIT, | 312 | BRCMF_FWS_FCMODE_IMPLIED_CREDIT, |
@@ -255,20 +320,28 @@ enum brcmf_fws_fcmode { | |||
255 | * @mac_handle: handle for mac entry determined by firmware. | 320 | * @mac_handle: handle for mac entry determined by firmware. |
256 | * @interface_id: interface index. | 321 | * @interface_id: interface index. |
257 | * @state: current state. | 322 | * @state: current state. |
323 | * @suppressed: mac entry is suppressed. | ||
324 | * @generation: generation bit. | ||
258 | * @ac_bitmap: ac queue bitmap. | 325 | * @ac_bitmap: ac queue bitmap. |
259 | * @requested_credit: credits requested by firmware. | 326 | * @requested_credit: credits requested by firmware. |
260 | * @ea: ethernet address. | 327 | * @ea: ethernet address. |
328 | * @seq: per-node free-running sequence. | ||
261 | * @psq: power-save queue. | 329 | * @psq: power-save queue. |
330 | * @transit_count: packet in transit to firmware. | ||
262 | */ | 331 | */ |
263 | struct brcmf_fws_mac_descriptor { | 332 | struct brcmf_fws_mac_descriptor { |
264 | u8 occupied; | 333 | u8 occupied; |
265 | u8 mac_handle; | 334 | u8 mac_handle; |
266 | u8 interface_id; | 335 | u8 interface_id; |
267 | u8 state; | 336 | u8 state; |
337 | bool suppressed; | ||
338 | u8 generation; | ||
268 | u8 ac_bitmap; | 339 | u8 ac_bitmap; |
269 | u8 requested_credit; | 340 | u8 requested_credit; |
270 | u8 ea[ETH_ALEN]; | 341 | u8 ea[ETH_ALEN]; |
342 | u8 seq[BRCMF_FWS_FIFO_COUNT]; | ||
271 | struct pktq psq; | 343 | struct pktq psq; |
344 | int transit_count; | ||
272 | }; | 345 | }; |
273 | 346 | ||
274 | #define BRCMF_FWS_HANGER_MAXITEMS 1024 | 347 | #define BRCMF_FWS_HANGER_MAXITEMS 1024 |
@@ -276,14 +349,14 @@ struct brcmf_fws_mac_descriptor { | |||
276 | /** | 349 | /** |
277 | * enum brcmf_fws_hanger_item_state - state of hanger item. | 350 | * enum brcmf_fws_hanger_item_state - state of hanger item. |
278 | * | 351 | * |
279 | * @WLFC_HANGER_ITEM_STATE_FREE: item is free for use. | 352 | * @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use. |
280 | * @WLFC_HANGER_ITEM_STATE_INUSE: item is in use. | 353 | * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use. |
281 | * @WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed. | 354 | * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed. |
282 | */ | 355 | */ |
283 | enum brcmf_fws_hanger_item_state { | 356 | enum brcmf_fws_hanger_item_state { |
284 | WLFC_HANGER_ITEM_STATE_FREE = 1, | 357 | BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1, |
285 | WLFC_HANGER_ITEM_STATE_INUSE, | 358 | BRCMF_FWS_HANGER_ITEM_STATE_INUSE, |
286 | WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED | 359 | BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED |
287 | }; | 360 | }; |
288 | 361 | ||
289 | 362 | ||
@@ -292,21 +365,17 @@ enum brcmf_fws_hanger_item_state { | |||
292 | * | 365 | * |
293 | * @state: entry is either free or occupied. | 366 | * @state: entry is either free or occupied. |
294 | * @gen: generation. | 367 | * @gen: generation. |
295 | * @identifier: packet identifier. | ||
296 | * @pkt: packet itself. | 368 | * @pkt: packet itself. |
297 | */ | 369 | */ |
298 | struct brcmf_fws_hanger_item { | 370 | struct brcmf_fws_hanger_item { |
299 | enum brcmf_fws_hanger_item_state state; | 371 | enum brcmf_fws_hanger_item_state state; |
300 | u8 gen; | 372 | u8 gen; |
301 | u8 pad[2]; | ||
302 | u32 identifier; | ||
303 | struct sk_buff *pkt; | 373 | struct sk_buff *pkt; |
304 | }; | 374 | }; |
305 | 375 | ||
306 | /** | 376 | /** |
307 | * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus. | 377 | * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus. |
308 | * | 378 | * |
309 | * @max_items: number of packets it can hold. | ||
310 | * @pushed: packets pushed to await txstatus. | 379 | * @pushed: packets pushed to await txstatus. |
311 | * @popped: packets popped upon handling txstatus. | 380 | * @popped: packets popped upon handling txstatus. |
312 | * @failed_to_push: packets that could not be pushed. | 381 | * @failed_to_push: packets that could not be pushed. |
@@ -332,20 +401,39 @@ struct brcmf_fws_info { | |||
332 | struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE]; | 401 | struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE]; |
333 | struct brcmf_fws_mac_descriptor other; | 402 | struct brcmf_fws_mac_descriptor other; |
334 | enum brcmf_fws_fcmode fcmode; | 403 | enum brcmf_fws_fcmode fcmode; |
335 | int fifo_credit[NL80211_NUM_ACS+1+1]; | 404 | int fifo_credit[BRCMF_FWS_FIFO_COUNT]; |
405 | }; | ||
406 | |||
407 | /* | ||
408 | * brcmf_fws_prio2fifo - mapping from 802.1d priority to firmware fifo index. | ||
409 | */ | ||
410 | static const int brcmf_fws_prio2fifo[] = { | ||
411 | BRCMF_FWS_FIFO_AC_BE, | ||
412 | BRCMF_FWS_FIFO_AC_BK, | ||
413 | BRCMF_FWS_FIFO_AC_BK, | ||
414 | BRCMF_FWS_FIFO_AC_BE, | ||
415 | BRCMF_FWS_FIFO_AC_VI, | ||
416 | BRCMF_FWS_FIFO_AC_VI, | ||
417 | BRCMF_FWS_FIFO_AC_VO, | ||
418 | BRCMF_FWS_FIFO_AC_VO | ||
336 | }; | 419 | }; |
337 | 420 | ||
338 | static int fcmode; | 421 | static int fcmode; |
339 | module_param(fcmode, int, S_IRUSR); | 422 | module_param(fcmode, int, S_IRUSR); |
340 | MODULE_PARM_DESC(fcmode, "mode of firmware signalled flow control"); | 423 | MODULE_PARM_DESC(fcmode, "mode of firmware signalled flow control"); |
341 | 424 | ||
342 | /** | ||
343 | * brcmf_fws_get_tlv_len() - returns defined length for given tlv id. | ||
344 | */ | ||
345 | #define BRCMF_FWS_TLV_DEF(name, id, len) \ | 425 | #define BRCMF_FWS_TLV_DEF(name, id, len) \ |
346 | case BRCMF_FWS_TYPE_ ## name: \ | 426 | case BRCMF_FWS_TYPE_ ## name: \ |
347 | return len; | 427 | return len; |
348 | 428 | ||
429 | /** | ||
430 | * brcmf_fws_get_tlv_len() - returns defined length for given tlv id. | ||
431 | * | ||
432 | * @fws: firmware-signalling information. | ||
433 | * @id: identifier of the TLV. | ||
434 | * | ||
435 | * Return: the specified length for the given TLV; Otherwise -EINVAL. | ||
436 | */ | ||
349 | static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws, | 437 | static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws, |
350 | enum brcmf_fws_tlv_type id) | 438 | enum brcmf_fws_tlv_type id) |
351 | { | 439 | { |
@@ -360,6 +448,30 @@ static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws, | |||
360 | } | 448 | } |
361 | #undef BRCMF_FWS_TLV_DEF | 449 | #undef BRCMF_FWS_TLV_DEF |
362 | 450 | ||
451 | static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg) | ||
452 | { | ||
453 | u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); | ||
454 | return ifidx == *(int *)arg; | ||
455 | } | ||
456 | |||
457 | static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, | ||
458 | int ifidx) | ||
459 | { | ||
460 | bool (*matchfn)(struct sk_buff *, void *) = NULL; | ||
461 | struct sk_buff *skb; | ||
462 | int prec; | ||
463 | |||
464 | if (ifidx != -1) | ||
465 | matchfn = brcmf_fws_ifidx_match; | ||
466 | for (prec = 0; prec < q->num_prec; prec++) { | ||
467 | skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); | ||
468 | while (skb) { | ||
469 | brcmf_txfinalize(fws->drvr, skb, false); | ||
470 | skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); | ||
471 | } | ||
472 | } | ||
473 | } | ||
474 | |||
363 | static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger) | 475 | static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger) |
364 | { | 476 | { |
365 | int i; | 477 | int i; |
@@ -367,10 +479,10 @@ static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger) | |||
367 | brcmf_dbg(TRACE, "enter\n"); | 479 | brcmf_dbg(TRACE, "enter\n"); |
368 | memset(hanger, 0, sizeof(*hanger)); | 480 | memset(hanger, 0, sizeof(*hanger)); |
369 | for (i = 0; i < ARRAY_SIZE(hanger->items); i++) | 481 | for (i = 0; i < ARRAY_SIZE(hanger->items); i++) |
370 | hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; | 482 | hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; |
371 | } | 483 | } |
372 | 484 | ||
373 | static __used u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) | 485 | static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) |
374 | { | 486 | { |
375 | u32 i; | 487 | u32 i; |
376 | 488 | ||
@@ -378,7 +490,7 @@ static __used u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) | |||
378 | i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS; | 490 | i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS; |
379 | 491 | ||
380 | while (i != h->slot_pos) { | 492 | while (i != h->slot_pos) { |
381 | if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) { | 493 | if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { |
382 | h->slot_pos = i; | 494 | h->slot_pos = i; |
383 | return i; | 495 | return i; |
384 | } | 496 | } |
@@ -391,7 +503,7 @@ static __used u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) | |||
391 | return BRCMF_FWS_HANGER_MAXITEMS; | 503 | return BRCMF_FWS_HANGER_MAXITEMS; |
392 | } | 504 | } |
393 | 505 | ||
394 | static __used int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, | 506 | static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, |
395 | struct sk_buff *pkt, u32 slot_id) | 507 | struct sk_buff *pkt, u32 slot_id) |
396 | { | 508 | { |
397 | brcmf_dbg(TRACE, "enter\n"); | 509 | brcmf_dbg(TRACE, "enter\n"); |
@@ -406,12 +518,11 @@ static __used int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, | |||
406 | 518 | ||
407 | h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE; | 519 | h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE; |
408 | h->items[slot_id].pkt = pkt; | 520 | h->items[slot_id].pkt = pkt; |
409 | h->items[slot_id].identifier = slot_id; | ||
410 | h->pushed++; | 521 | h->pushed++; |
411 | return 0; | 522 | return 0; |
412 | } | 523 | } |
413 | 524 | ||
414 | static __used int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, | 525 | static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, |
415 | u32 slot_id, struct sk_buff **pktout, | 526 | u32 slot_id, struct sk_buff **pktout, |
416 | bool remove_item) | 527 | bool remove_item) |
417 | { | 528 | { |
@@ -429,7 +540,6 @@ static __used int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, | |||
429 | if (remove_item) { | 540 | if (remove_item) { |
430 | h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; | 541 | h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; |
431 | h->items[slot_id].pkt = NULL; | 542 | h->items[slot_id].pkt = NULL; |
432 | h->items[slot_id].identifier = 0; | ||
433 | h->items[slot_id].gen = 0xff; | 543 | h->items[slot_id].gen = 0xff; |
434 | h->popped++; | 544 | h->popped++; |
435 | } | 545 | } |
@@ -446,16 +556,16 @@ static __used int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, | |||
446 | 556 | ||
447 | h->items[slot_id].gen = gen; | 557 | h->items[slot_id].gen = gen; |
448 | 558 | ||
449 | if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_INUSE) { | 559 | if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_INUSE) { |
450 | brcmf_err("entry not in use\n"); | 560 | brcmf_err("entry not in use\n"); |
451 | return -EINVAL; | 561 | return -EINVAL; |
452 | } | 562 | } |
453 | 563 | ||
454 | h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED; | 564 | h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED; |
455 | return 0; | 565 | return 0; |
456 | } | 566 | } |
457 | 567 | ||
458 | static __used int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger, | 568 | static int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger, |
459 | struct sk_buff *pkt, u32 slot_id, | 569 | struct sk_buff *pkt, u32 slot_id, |
460 | int *gen) | 570 | int *gen) |
461 | { | 571 | { |
@@ -542,17 +652,50 @@ brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea) | |||
542 | return ERR_PTR(-ENOENT); | 652 | return ERR_PTR(-ENOENT); |
543 | } | 653 | } |
544 | 654 | ||
545 | static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_mac_descriptor *entry, | 655 | static struct brcmf_fws_mac_descriptor* |
546 | bool (*fn)(struct sk_buff *, void *), | 656 | brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, int ifidx, u8 *da) |
657 | { | ||
658 | struct brcmf_fws_mac_descriptor *entry = &fws->other; | ||
659 | struct brcmf_if *ifp; | ||
660 | bool multicast; | ||
661 | |||
662 | brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); | ||
663 | |||
664 | multicast = is_multicast_ether_addr(da); | ||
665 | ifp = fws->drvr->iflist[ifidx ? ifidx + 1 : 0]; | ||
666 | if (WARN_ON(!ifp)) | ||
667 | goto done; | ||
668 | |||
669 | /* Multicast destination and P2P clients get the interface entry. | ||
670 | * STA gets the interface entry if there is no exact match. For | ||
671 | * example, TDLS destinations have their own entry. | ||
672 | */ | ||
673 | entry = NULL; | ||
674 | if ((/* ifp->iftype == 0 ||*/ multicast) && ifp->fws_desc) | ||
675 | entry = ifp->fws_desc; | ||
676 | |||
677 | if (entry != NULL && multicast) | ||
678 | goto done; | ||
679 | |||
680 | entry = brcmf_fws_mac_descriptor_lookup(fws, da); | ||
681 | if (IS_ERR(entry)) | ||
682 | entry = &fws->other; | ||
683 | |||
684 | done: | ||
685 | brcmf_dbg(TRACE, "exit: entry=%p\n", entry); | ||
686 | return entry; | ||
687 | } | ||
688 | |||
689 | static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws, | ||
690 | struct brcmf_fws_mac_descriptor *entry, | ||
547 | int ifidx) | 691 | int ifidx) |
548 | { | 692 | { |
549 | brcmf_dbg(TRACE, "enter: entry=(ea=%pM,ifid=%d), ifidx=%d\n", | 693 | brcmf_dbg(TRACE, "enter: entry=(ea=%pM, ifid=%d), ifidx=%d\n", |
550 | entry->ea, entry->interface_id, ifidx); | 694 | entry->ea, entry->interface_id, ifidx); |
551 | if (entry->occupied && (fn == NULL || (ifidx == entry->interface_id))) { | 695 | if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) { |
552 | brcmf_dbg(TRACE, "flush delayQ: ifidx=%d, qlen=%d\n", | 696 | brcmf_dbg(TRACE, "flush psq: ifidx=%d, qlen=%d\n", |
553 | ifidx, entry->psq.len); | 697 | ifidx, entry->psq.len); |
554 | /* release packets held in DELAYQ */ | 698 | brcmf_fws_psq_flush(fws, &entry->psq, ifidx); |
555 | brcmu_pktq_flush(&entry->psq, true, fn, &ifidx); | ||
556 | entry->occupied = !!(entry->psq.len); | 699 | entry->occupied = !!(entry->psq.len); |
557 | } | 700 | } |
558 | } | 701 | } |
@@ -587,12 +730,6 @@ static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws, | |||
587 | } | 730 | } |
588 | } | 731 | } |
589 | 732 | ||
590 | static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg) | ||
591 | { | ||
592 | u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); | ||
593 | return ifidx == *(int *)arg; | ||
594 | } | ||
595 | |||
596 | static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) | 733 | static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) |
597 | { | 734 | { |
598 | int i; | 735 | int i; |
@@ -609,9 +746,9 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) | |||
609 | /* cleanup individual nodes */ | 746 | /* cleanup individual nodes */ |
610 | table = &fws->nodes[0]; | 747 | table = &fws->nodes[0]; |
611 | for (i = 0; i < ARRAY_SIZE(fws->nodes); i++) | 748 | for (i = 0; i < ARRAY_SIZE(fws->nodes); i++) |
612 | brcmf_fws_mac_desc_cleanup(&table[i], matchfn, ifidx); | 749 | brcmf_fws_mac_desc_cleanup(fws, &table[i], ifidx); |
613 | 750 | ||
614 | brcmf_fws_mac_desc_cleanup(&fws->other, matchfn, ifidx); | 751 | brcmf_fws_mac_desc_cleanup(fws, &fws->other, ifidx); |
615 | brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx); | 752 | brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx); |
616 | brcmf_fws_hanger_cleanup(&fws->hanger, matchfn, ifidx); | 753 | brcmf_fws_hanger_cleanup(&fws->hanger, matchfn, ifidx); |
617 | } | 754 | } |
@@ -637,17 +774,15 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) | |||
637 | entry = &fws->nodes[mac_handle & 0x1F]; | 774 | entry = &fws->nodes[mac_handle & 0x1F]; |
638 | if (type == BRCMF_FWS_TYPE_MACDESC_DEL) { | 775 | if (type == BRCMF_FWS_TYPE_MACDESC_DEL) { |
639 | brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx); | 776 | brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx); |
640 | if (entry->occupied) { | 777 | if (entry->occupied) |
641 | entry->occupied = 0; | 778 | brcmf_fws_clear_mac_descriptor(entry); |
642 | entry->state = BRCMF_FWS_STATE_CLOSE; | 779 | else |
643 | entry->requested_credit = 0; | ||
644 | } else { | ||
645 | fws->stats.mac_update_failed++; | 780 | fws->stats.mac_update_failed++; |
646 | } | ||
647 | return 0; | 781 | return 0; |
648 | } | 782 | } |
649 | 783 | ||
650 | brcmf_dbg(TRACE, "add mac %pM idx %d\n", addr, ifidx); | 784 | brcmf_dbg(TRACE, |
785 | "add mac %pM handle %u idx %d\n", addr, mac_handle, ifidx); | ||
651 | existing = brcmf_fws_mac_descriptor_lookup(fws, addr); | 786 | existing = brcmf_fws_mac_descriptor_lookup(fws, addr); |
652 | if (IS_ERR(existing)) { | 787 | if (IS_ERR(existing)) { |
653 | if (!entry->occupied) { | 788 | if (!entry->occupied) { |
@@ -674,6 +809,51 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) | |||
674 | return 0; | 809 | return 0; |
675 | } | 810 | } |
676 | 811 | ||
812 | static int | ||
813 | brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) | ||
814 | { | ||
815 | u8 flags; | ||
816 | u32 status; | ||
817 | u32 hslot; | ||
818 | int ret; | ||
819 | struct sk_buff *skb; | ||
820 | struct brcmf_fws_mac_descriptor *entry = NULL; | ||
821 | |||
822 | status = le32_to_cpu(*(__le32 *)data); | ||
823 | flags = brcmf_txstatus_get_field(status, FLAGS); | ||
824 | hslot = brcmf_txstatus_get_field(status, HSLOT); | ||
825 | fws->stats.txs_indicate++; | ||
826 | |||
827 | brcmf_dbg(TRACE, "status: flags=0x%X, hslot=%d\n", | ||
828 | flags, hslot); | ||
829 | |||
830 | if (flags == BRCMF_FWS_TXSTATUS_DISCARD) | ||
831 | fws->stats.txs_discard++; | ||
832 | else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) | ||
833 | fws->stats.txs_tossed++; | ||
834 | else | ||
835 | brcmf_err("unexpected txstatus\n"); | ||
836 | |||
837 | ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, | ||
838 | true); | ||
839 | if (ret != 0) { | ||
840 | brcmf_err("no packet in hanger slot: hslot=%d\n", hslot); | ||
841 | return ret; | ||
842 | } | ||
843 | |||
844 | entry = brcmf_skbcb(skb)->mac; | ||
845 | if (WARN_ON(!entry)) { | ||
846 | ret = -EINVAL; | ||
847 | goto done; | ||
848 | } | ||
849 | |||
850 | entry->transit_count--; | ||
851 | |||
852 | done: | ||
853 | brcmf_txfinalize(fws->drvr, skb, true); | ||
854 | return ret; | ||
855 | } | ||
856 | |||
677 | static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) | 857 | static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) |
678 | { | 858 | { |
679 | __le32 timestamp; | 859 | __le32 timestamp; |
@@ -723,7 +903,8 @@ int brcmf_fws_init(struct brcmf_pub *drvr) | |||
723 | /* enable rssi signals */ | 903 | /* enable rssi signals */ |
724 | if (drvr->fw_signals) | 904 | if (drvr->fw_signals) |
725 | tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS | | 905 | tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS | |
726 | BRCMF_FWS_FLAGS_XONXOFF_SIGNALS; | 906 | BRCMF_FWS_FLAGS_XONXOFF_SIGNALS | |
907 | BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS; | ||
727 | 908 | ||
728 | spin_lock_init(&drvr->fws_spinlock); | 909 | spin_lock_init(&drvr->fws_spinlock); |
729 | 910 | ||
@@ -839,7 +1020,6 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, | |||
839 | case BRCMF_FWS_TYPE_MAC_OPEN: | 1020 | case BRCMF_FWS_TYPE_MAC_OPEN: |
840 | case BRCMF_FWS_TYPE_MAC_CLOSE: | 1021 | case BRCMF_FWS_TYPE_MAC_CLOSE: |
841 | case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT: | 1022 | case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT: |
842 | case BRCMF_FWS_TYPE_TXSTATUS: | ||
843 | case BRCMF_FWS_TYPE_PKTTAG: | 1023 | case BRCMF_FWS_TYPE_PKTTAG: |
844 | case BRCMF_FWS_TYPE_INTERFACE_OPEN: | 1024 | case BRCMF_FWS_TYPE_INTERFACE_OPEN: |
845 | case BRCMF_FWS_TYPE_INTERFACE_CLOSE: | 1025 | case BRCMF_FWS_TYPE_INTERFACE_CLOSE: |
@@ -853,6 +1033,9 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, | |||
853 | case BRCMF_FWS_TYPE_MACDESC_DEL: | 1033 | case BRCMF_FWS_TYPE_MACDESC_DEL: |
854 | brcmf_fws_macdesc_indicate(fws, type, data); | 1034 | brcmf_fws_macdesc_indicate(fws, type, data); |
855 | break; | 1035 | break; |
1036 | case BRCMF_FWS_TYPE_TXSTATUS: | ||
1037 | brcmf_fws_txstatus_indicate(fws, data); | ||
1038 | break; | ||
856 | case BRCMF_FWS_TYPE_RSSI: | 1039 | case BRCMF_FWS_TYPE_RSSI: |
857 | brcmf_fws_rssi_indicate(fws, *data); | 1040 | brcmf_fws_rssi_indicate(fws, *data); |
858 | break; | 1041 | break; |
@@ -885,6 +1068,174 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, | |||
885 | return 0; | 1068 | return 0; |
886 | } | 1069 | } |
887 | 1070 | ||
1071 | static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) | ||
1072 | { | ||
1073 | struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; | ||
1074 | u8 *wlh; | ||
1075 | u16 data_offset; | ||
1076 | u8 fillers; | ||
1077 | __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); | ||
1078 | |||
1079 | brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u, pkttag=0x%08X\n", | ||
1080 | entry->ea, entry->interface_id, le32_to_cpu(pkttag)); | ||
1081 | /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ | ||
1082 | data_offset = 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; | ||
1083 | fillers = round_up(data_offset, 4) - data_offset; | ||
1084 | data_offset += fillers; | ||
1085 | |||
1086 | skb_push(skb, data_offset); | ||
1087 | wlh = skb->data; | ||
1088 | |||
1089 | wlh[0] = BRCMF_FWS_TYPE_PKTTAG; | ||
1090 | wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN; | ||
1091 | memcpy(&wlh[2], &pkttag, sizeof(pkttag)); | ||
1092 | wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2; | ||
1093 | |||
1094 | if (fillers) | ||
1095 | memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); | ||
1096 | |||
1097 | brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX), | ||
1098 | data_offset >> 2, skb); | ||
1099 | return 0; | ||
1100 | } | ||
1101 | |||
1102 | static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, | ||
1103 | struct sk_buff *p) | ||
1104 | { | ||
1105 | struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); | ||
1106 | struct brcmf_fws_mac_descriptor *entry = skcb->mac; | ||
1107 | int rc = 0; | ||
1108 | bool header_needed; | ||
1109 | int hslot = BRCMF_FWS_HANGER_MAXITEMS; | ||
1110 | u8 free_ctr; | ||
1111 | u8 ifidx; | ||
1112 | u8 flags; | ||
1113 | |||
1114 | header_needed = skcb->state == BRCMF_FWS_SKBSTATE_NEW; | ||
1115 | |||
1116 | if (header_needed) { | ||
1117 | /* obtaining free slot may fail, but that will be caught | ||
1118 | * by the hanger push. This assures the packet has a BDC | ||
1119 | * header upon return. | ||
1120 | */ | ||
1121 | hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger); | ||
1122 | free_ctr = entry->seq[fifo]; | ||
1123 | brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); | ||
1124 | brcmf_skb_htod_tag_set_field(p, FREERUN, free_ctr); | ||
1125 | brcmf_skb_htod_tag_set_field(p, GENERATION, 1); | ||
1126 | entry->transit_count++; | ||
1127 | } | ||
1128 | brcmf_skb_if_flags_set_field(p, TRANSMIT, 1); | ||
1129 | brcmf_skb_htod_tag_set_field(p, FIFO, fifo); | ||
1130 | |||
1131 | flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST; | ||
1132 | if (!(skcb->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) { | ||
1133 | /* | ||
1134 | Indicate that this packet is being sent in response to an | ||
1135 | explicit request from the firmware side. | ||
1136 | */ | ||
1137 | flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED; | ||
1138 | } | ||
1139 | brcmf_skb_htod_tag_set_field(p, FLAGS, flags); | ||
1140 | if (header_needed) { | ||
1141 | brcmf_fws_hdrpush(fws, p); | ||
1142 | rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot); | ||
1143 | if (rc) | ||
1144 | brcmf_err("hanger push failed: rc=%d\n", rc); | ||
1145 | } else { | ||
1146 | int gen; | ||
1147 | |||
1148 | /* remove old header */ | ||
1149 | rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, p); | ||
1150 | if (rc == 0) { | ||
1151 | hslot = brcmf_skb_htod_tag_get_field(p, HSLOT); | ||
1152 | brcmf_fws_hanger_get_genbit(&fws->hanger, p, | ||
1153 | hslot, &gen); | ||
1154 | brcmf_skb_htod_tag_set_field(p, GENERATION, gen); | ||
1155 | |||
1156 | /* push new header */ | ||
1157 | brcmf_fws_hdrpush(fws, p); | ||
1158 | } | ||
1159 | } | ||
1160 | |||
1161 | return rc; | ||
1162 | } | ||
1163 | |||
1164 | static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, | ||
1165 | struct sk_buff *skb) | ||
1166 | { | ||
1167 | struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); | ||
1168 | struct brcmf_fws_mac_descriptor *entry; | ||
1169 | struct brcmf_bus *bus = fws->drvr->bus_if; | ||
1170 | int rc; | ||
1171 | |||
1172 | entry = skcb->mac; | ||
1173 | if (IS_ERR(entry)) | ||
1174 | return PTR_ERR(entry); | ||
1175 | |||
1176 | rc = brcmf_fws_precommit_skb(fws, fifo, skb); | ||
1177 | if (rc < 0) { | ||
1178 | fws->stats.generic_error++; | ||
1179 | goto done; | ||
1180 | } | ||
1181 | |||
1182 | rc = brcmf_bus_txdata(bus, skb); | ||
1183 | if (rc < 0) | ||
1184 | goto done; | ||
1185 | |||
1186 | entry->seq[fifo]++; | ||
1187 | fws->stats.pkt2bus++; | ||
1188 | return rc; | ||
1189 | |||
1190 | done: | ||
1191 | brcmf_txfinalize(fws->drvr, skb, false); | ||
1192 | return rc; | ||
1193 | } | ||
1194 | |||
1195 | int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) | ||
1196 | { | ||
1197 | struct brcmf_pub *drvr = ifp->drvr; | ||
1198 | struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); | ||
1199 | struct ethhdr *eh = (struct ethhdr *)(skb->data); | ||
1200 | ulong flags; | ||
1201 | u8 ifidx = ifp->ifidx; | ||
1202 | int fifo = BRCMF_FWS_FIFO_BCMC; | ||
1203 | bool multicast = is_multicast_ether_addr(eh->h_dest); | ||
1204 | |||
1205 | /* determine the priority */ | ||
1206 | if (!skb->priority) | ||
1207 | skb->priority = cfg80211_classify8021d(skb); | ||
1208 | |||
1209 | drvr->tx_multicast += !!multicast; | ||
1210 | if (ntohs(eh->h_proto) == ETH_P_PAE) | ||
1211 | atomic_inc(&ifp->pend_8021x_cnt); | ||
1212 | |||
1213 | if (!brcmf_fws_fc_active(drvr->fws)) { | ||
1214 | /* If the protocol uses a data header, apply it */ | ||
1215 | brcmf_proto_hdrpush(drvr, ifidx, 0, skb); | ||
1216 | |||
1217 | /* Use bus module to send data frame */ | ||
1218 | return brcmf_bus_txdata(drvr->bus_if, skb); | ||
1219 | } | ||
1220 | |||
1221 | /* set control buffer information */ | ||
1222 | skcb->mac = brcmf_fws_find_mac_desc(drvr->fws, ifidx, eh->h_dest); | ||
1223 | skcb->state = BRCMF_FWS_SKBSTATE_NEW; | ||
1224 | brcmf_skb_if_flags_set_field(skb, INDEX, ifidx); | ||
1225 | if (!multicast) | ||
1226 | fifo = brcmf_fws_prio2fifo[skb->priority]; | ||
1227 | brcmf_skb_if_flags_set_field(skb, FIFO, fifo); | ||
1228 | brcmf_skb_if_flags_set_field(skb, CREDITCHECK, 0); | ||
1229 | |||
1230 | brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest, | ||
1231 | multicast, fifo); | ||
1232 | |||
1233 | brcmf_fws_lock(drvr, flags); | ||
1234 | brcmf_fws_commit_skb(drvr->fws, fifo, skb); | ||
1235 | brcmf_fws_unlock(drvr, flags); | ||
1236 | return 0; | ||
1237 | } | ||
1238 | |||
888 | void brcmf_fws_reset_interface(struct brcmf_if *ifp) | 1239 | void brcmf_fws_reset_interface(struct brcmf_if *ifp) |
889 | { | 1240 | { |
890 | struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; | 1241 | struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h index 1566f4de0eda..7778e02d7581 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h | |||
@@ -23,6 +23,7 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr); | |||
23 | bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); | 23 | bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); |
24 | int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, | 24 | int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, |
25 | struct sk_buff *skb); | 25 | struct sk_buff *skb); |
26 | int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb); | ||
26 | 27 | ||
27 | void brcmf_fws_reset_interface(struct brcmf_if *ifp); | 28 | void brcmf_fws_reset_interface(struct brcmf_if *ifp); |
28 | void brcmf_fws_add_interface(struct brcmf_if *ifp); | 29 | void brcmf_fws_add_interface(struct brcmf_if *ifp); |