1*4882a593Smuzhiyun // SPDX-License-Identifier: ISC
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2010 Broadcom Corporation
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun #include <linux/types.h>
6*4882a593Smuzhiyun #include <linux/module.h>
7*4882a593Smuzhiyun #include <linux/if_ether.h>
8*4882a593Smuzhiyun #include <linux/spinlock.h>
9*4882a593Smuzhiyun #include <linux/skbuff.h>
10*4882a593Smuzhiyun #include <linux/netdevice.h>
11*4882a593Smuzhiyun #include <linux/etherdevice.h>
12*4882a593Smuzhiyun #include <linux/err.h>
13*4882a593Smuzhiyun #include <linux/jiffies.h>
14*4882a593Smuzhiyun #include <net/cfg80211.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <brcmu_utils.h>
17*4882a593Smuzhiyun #include <brcmu_wifi.h>
18*4882a593Smuzhiyun #include "core.h"
19*4882a593Smuzhiyun #include "debug.h"
20*4882a593Smuzhiyun #include "bus.h"
21*4882a593Smuzhiyun #include "fwil.h"
22*4882a593Smuzhiyun #include "fwil_types.h"
23*4882a593Smuzhiyun #include "fweh.h"
24*4882a593Smuzhiyun #include "fwsignal.h"
25*4882a593Smuzhiyun #include "p2p.h"
26*4882a593Smuzhiyun #include "cfg80211.h"
27*4882a593Smuzhiyun #include "proto.h"
28*4882a593Smuzhiyun #include "bcdc.h"
29*4882a593Smuzhiyun #include "common.h"
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun /**
32*4882a593Smuzhiyun * DOC: Firmware Signalling
33*4882a593Smuzhiyun *
34*4882a593Smuzhiyun * Firmware can send signals to host and vice versa, which are passed in the
35*4882a593Smuzhiyun * data packets using TLV based header. This signalling layer is on top of the
36*4882a593Smuzhiyun * BDC bus protocol layer.
37*4882a593Smuzhiyun */
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun /*
40*4882a593Smuzhiyun * single definition for firmware-driver flow control tlv's.
41*4882a593Smuzhiyun *
42*4882a593Smuzhiyun * each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length).
43*4882a593Smuzhiyun * A length value 0 indicates variable length tlv.
44*4882a593Smuzhiyun */
45*4882a593Smuzhiyun #define BRCMF_FWS_TLV_DEFLIST \
46*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \
47*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \
48*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \
49*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \
50*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \
51*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(MACDESC_ADD, 6, 8) \
52*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \
53*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \
54*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \
55*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \
56*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \
57*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \
58*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \
59*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \
60*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \
61*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \
62*4882a593Smuzhiyun BRCMF_FWS_TLV_DEF(FILLER, 255, 0)
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun /*
65*4882a593Smuzhiyun * enum brcmf_fws_tlv_type - definition of tlv identifiers.
66*4882a593Smuzhiyun */
67*4882a593Smuzhiyun #define BRCMF_FWS_TLV_DEF(name, id, len) \
68*4882a593Smuzhiyun BRCMF_FWS_TYPE_ ## name = id,
69*4882a593Smuzhiyun enum brcmf_fws_tlv_type {
70*4882a593Smuzhiyun BRCMF_FWS_TLV_DEFLIST
71*4882a593Smuzhiyun BRCMF_FWS_TYPE_INVALID
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun #undef BRCMF_FWS_TLV_DEF
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun /*
76*4882a593Smuzhiyun * enum brcmf_fws_tlv_len - definition of tlv lengths.
77*4882a593Smuzhiyun */
78*4882a593Smuzhiyun #define BRCMF_FWS_TLV_DEF(name, id, len) \
79*4882a593Smuzhiyun BRCMF_FWS_TYPE_ ## name ## _LEN = (len),
80*4882a593Smuzhiyun enum brcmf_fws_tlv_len {
81*4882a593Smuzhiyun BRCMF_FWS_TLV_DEFLIST
82*4882a593Smuzhiyun };
83*4882a593Smuzhiyun #undef BRCMF_FWS_TLV_DEF
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun /* AMPDU rx reordering definitions */
86*4882a593Smuzhiyun #define BRCMF_RXREORDER_FLOWID_OFFSET 0
87*4882a593Smuzhiyun #define BRCMF_RXREORDER_MAXIDX_OFFSET 2
88*4882a593Smuzhiyun #define BRCMF_RXREORDER_FLAGS_OFFSET 4
89*4882a593Smuzhiyun #define BRCMF_RXREORDER_CURIDX_OFFSET 6
90*4882a593Smuzhiyun #define BRCMF_RXREORDER_EXPIDX_OFFSET 8
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun #define BRCMF_RXREORDER_DEL_FLOW 0x01
93*4882a593Smuzhiyun #define BRCMF_RXREORDER_FLUSH_ALL 0x02
94*4882a593Smuzhiyun #define BRCMF_RXREORDER_CURIDX_VALID 0x04
95*4882a593Smuzhiyun #define BRCMF_RXREORDER_EXPIDX_VALID 0x08
96*4882a593Smuzhiyun #define BRCMF_RXREORDER_NEW_HOLE 0x10
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun #ifdef DEBUG
99*4882a593Smuzhiyun /*
100*4882a593Smuzhiyun * brcmf_fws_tlv_names - array of tlv names.
101*4882a593Smuzhiyun */
102*4882a593Smuzhiyun #define BRCMF_FWS_TLV_DEF(name, id, len) \
103*4882a593Smuzhiyun { id, #name },
104*4882a593Smuzhiyun static struct {
105*4882a593Smuzhiyun enum brcmf_fws_tlv_type id;
106*4882a593Smuzhiyun const char *name;
107*4882a593Smuzhiyun } brcmf_fws_tlv_names[] = {
108*4882a593Smuzhiyun BRCMF_FWS_TLV_DEFLIST
109*4882a593Smuzhiyun };
110*4882a593Smuzhiyun #undef BRCMF_FWS_TLV_DEF
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun
brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)113*4882a593Smuzhiyun static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun int i;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++)
118*4882a593Smuzhiyun if (brcmf_fws_tlv_names[i].id == id)
119*4882a593Smuzhiyun return brcmf_fws_tlv_names[i].name;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun return "INVALID";
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun #else
brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)124*4882a593Smuzhiyun static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun return "NODEBUG";
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun #endif /* DEBUG */
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun /*
131*4882a593Smuzhiyun * The PKTTAG tlv has additional bytes when firmware-signalling
132*4882a593Smuzhiyun * mode has REUSESEQ flag set.
133*4882a593Smuzhiyun */
134*4882a593Smuzhiyun #define BRCMF_FWS_TYPE_SEQ_LEN 2
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun /*
137*4882a593Smuzhiyun * flags used to enable tlv signalling from firmware.
138*4882a593Smuzhiyun */
139*4882a593Smuzhiyun #define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001
140*4882a593Smuzhiyun #define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002
141*4882a593Smuzhiyun #define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004
142*4882a593Smuzhiyun #define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008
143*4882a593Smuzhiyun #define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010
144*4882a593Smuzhiyun #define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020
145*4882a593Smuzhiyun #define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun #define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32
148*4882a593Smuzhiyun #define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun #define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0
151*4882a593Smuzhiyun #define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1
152*4882a593Smuzhiyun #define BRCMF_FWS_FLOWCONTROL_HIWATER 128
153*4882a593Smuzhiyun #define BRCMF_FWS_FLOWCONTROL_LOWATER 64
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun #define BRCMF_FWS_PSQ_PREC_COUNT ((BRCMF_FWS_FIFO_COUNT + 1) * 2)
156*4882a593Smuzhiyun #define BRCMF_FWS_PSQ_LEN 256
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun #define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01
159*4882a593Smuzhiyun #define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun #define BRCMF_FWS_RET_OK_NOSCHEDULE 0
162*4882a593Smuzhiyun #define BRCMF_FWS_RET_OK_SCHEDULE 1
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun #define BRCMF_FWS_MODE_REUSESEQ_SHIFT 3 /* seq reuse */
165*4882a593Smuzhiyun #define BRCMF_FWS_MODE_SET_REUSESEQ(x, val) ((x) = \
166*4882a593Smuzhiyun ((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \
167*4882a593Smuzhiyun (((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT))
168*4882a593Smuzhiyun #define BRCMF_FWS_MODE_GET_REUSESEQ(x) \
169*4882a593Smuzhiyun (((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1)
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun /**
172*4882a593Smuzhiyun * enum brcmf_fws_skb_state - indicates processing state of skb.
173*4882a593Smuzhiyun *
174*4882a593Smuzhiyun * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver.
175*4882a593Smuzhiyun * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue.
176*4882a593Smuzhiyun * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware.
177*4882a593Smuzhiyun * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info.
178*4882a593Smuzhiyun */
179*4882a593Smuzhiyun enum brcmf_fws_skb_state {
180*4882a593Smuzhiyun BRCMF_FWS_SKBSTATE_NEW,
181*4882a593Smuzhiyun BRCMF_FWS_SKBSTATE_DELAYED,
182*4882a593Smuzhiyun BRCMF_FWS_SKBSTATE_SUPPRESSED,
183*4882a593Smuzhiyun BRCMF_FWS_SKBSTATE_TIM
184*4882a593Smuzhiyun };
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun /**
187*4882a593Smuzhiyun * struct brcmf_skbuff_cb - control buffer associated with skbuff.
188*4882a593Smuzhiyun *
189*4882a593Smuzhiyun * @bus_flags: 2 bytes reserved for bus specific parameters
190*4882a593Smuzhiyun * @if_flags: holds interface index and packet related flags.
191*4882a593Smuzhiyun * @htod: host to device packet identifier (used in PKTTAG tlv).
192*4882a593Smuzhiyun * @htod_seq: this 16-bit is original seq number for every suppress packet.
193*4882a593Smuzhiyun * @state: transmit state of the packet.
194*4882a593Smuzhiyun * @mac: descriptor related to destination for this packet.
195*4882a593Smuzhiyun *
196*4882a593Smuzhiyun * This information is stored in control buffer struct sk_buff::cb, which
197*4882a593Smuzhiyun * provides 48 bytes of storage so this structure should not exceed that.
198*4882a593Smuzhiyun */
199*4882a593Smuzhiyun struct brcmf_skbuff_cb {
200*4882a593Smuzhiyun u16 bus_flags;
201*4882a593Smuzhiyun u16 if_flags;
202*4882a593Smuzhiyun u32 htod;
203*4882a593Smuzhiyun u16 htod_seq;
204*4882a593Smuzhiyun enum brcmf_fws_skb_state state;
205*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *mac;
206*4882a593Smuzhiyun };
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun /*
209*4882a593Smuzhiyun * macro casting skbuff control buffer to struct brcmf_skbuff_cb.
210*4882a593Smuzhiyun */
211*4882a593Smuzhiyun #define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb))
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun /*
214*4882a593Smuzhiyun * sk_buff control if flags
215*4882a593Smuzhiyun *
216*4882a593Smuzhiyun * b[11] - packet sent upon firmware request.
217*4882a593Smuzhiyun * b[10] - packet only contains signalling data.
218*4882a593Smuzhiyun * b[9] - packet is a tx packet.
219*4882a593Smuzhiyun * b[8] - packet used requested credit
220*4882a593Smuzhiyun * b[7] - interface in AP mode.
221*4882a593Smuzhiyun * b[3:0] - interface index.
222*4882a593Smuzhiyun */
223*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800
224*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11
225*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400
226*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT 10
227*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK 0x0200
228*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT 9
229*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK 0x0100
230*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT 8
231*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_IF_AP_MASK 0x0080
232*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT 7
233*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_INDEX_MASK 0x000f
234*4882a593Smuzhiyun #define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun #define brcmf_skb_if_flags_set_field(skb, field, value) \
237*4882a593Smuzhiyun brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \
238*4882a593Smuzhiyun BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
239*4882a593Smuzhiyun BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value))
240*4882a593Smuzhiyun #define brcmf_skb_if_flags_get_field(skb, field) \
241*4882a593Smuzhiyun brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \
242*4882a593Smuzhiyun BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
243*4882a593Smuzhiyun BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT)
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun /*
246*4882a593Smuzhiyun * sk_buff control packet identifier
247*4882a593Smuzhiyun *
248*4882a593Smuzhiyun * 32-bit packet identifier used in PKTTAG tlv from host to dongle.
249*4882a593Smuzhiyun *
250*4882a593Smuzhiyun * - Generated at the host (e.g. dhd)
251*4882a593Smuzhiyun * - Seen as a generic sequence number by firmware except for the flags field.
252*4882a593Smuzhiyun *
253*4882a593Smuzhiyun * Generation : b[31] => generation number for this packet [host->fw]
254*4882a593Smuzhiyun * OR, current generation number [fw->host]
255*4882a593Smuzhiyun * Flags : b[30:27] => command, status flags
256*4882a593Smuzhiyun * FIFO-AC : b[26:24] => AC-FIFO id
257*4882a593Smuzhiyun * h-slot : b[23:8] => hanger-slot
258*4882a593Smuzhiyun * freerun : b[7:0] => A free running counter
259*4882a593Smuzhiyun */
260*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_TAG_GENERATION_MASK 0x80000000
261*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT 31
262*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_TAG_FLAGS_MASK 0x78000000
263*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT 27
264*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_TAG_FIFO_MASK 0x07000000
265*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT 24
266*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_TAG_HSLOT_MASK 0x00ffff00
267*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT 8
268*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_TAG_FREERUN_MASK 0x000000ff
269*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun #define brcmf_skb_htod_tag_set_field(skb, field, value) \
272*4882a593Smuzhiyun brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \
273*4882a593Smuzhiyun BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
274*4882a593Smuzhiyun BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value))
275*4882a593Smuzhiyun #define brcmf_skb_htod_tag_get_field(skb, field) \
276*4882a593Smuzhiyun brcmu_maskget32(brcmf_skbcb(skb)->htod, \
277*4882a593Smuzhiyun BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
278*4882a593Smuzhiyun BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT)
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK 0x2000
281*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT 13
282*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK 0x1000
283*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT 12
284*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_SEQ_NR_MASK 0x0fff
285*4882a593Smuzhiyun #define BRCMF_SKB_HTOD_SEQ_NR_SHIFT 0
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun #define brcmf_skb_htod_seq_set_field(skb, field, value) \
288*4882a593Smuzhiyun brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \
289*4882a593Smuzhiyun BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
290*4882a593Smuzhiyun BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value))
291*4882a593Smuzhiyun #define brcmf_skb_htod_seq_get_field(skb, field) \
292*4882a593Smuzhiyun brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \
293*4882a593Smuzhiyun BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
294*4882a593Smuzhiyun BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT)
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun #define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000
297*4882a593Smuzhiyun #define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31
298*4882a593Smuzhiyun #define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000
299*4882a593Smuzhiyun #define BRCMF_FWS_TXSTAT_FLAGS_SHIFT 27
300*4882a593Smuzhiyun #define BRCMF_FWS_TXSTAT_FIFO_MASK 0x07000000
301*4882a593Smuzhiyun #define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24
302*4882a593Smuzhiyun #define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00
303*4882a593Smuzhiyun #define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8
304*4882a593Smuzhiyun #define BRCMF_FWS_TXSTAT_FREERUN_MASK 0x000000FF
305*4882a593Smuzhiyun #define BRCMF_FWS_TXSTAT_FREERUN_SHIFT 0
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun #define brcmf_txstatus_get_field(txs, field) \
308*4882a593Smuzhiyun brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
309*4882a593Smuzhiyun BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun /* How long to defer borrowing in jiffies */
312*4882a593Smuzhiyun #define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10)
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun /**
316*4882a593Smuzhiyun * enum brcmf_fws_txstatus - txstatus flag values.
317*4882a593Smuzhiyun *
318*4882a593Smuzhiyun * @BRCMF_FWS_TXSTATUS_DISCARD:
319*4882a593Smuzhiyun * host is free to discard the packet.
320*4882a593Smuzhiyun * @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS:
321*4882a593Smuzhiyun * 802.11 core suppressed the packet.
322*4882a593Smuzhiyun * @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS:
323*4882a593Smuzhiyun * firmware suppress the packet as device is already in PS mode.
324*4882a593Smuzhiyun * @BRCMF_FWS_TXSTATUS_FW_TOSSED:
325*4882a593Smuzhiyun * firmware tossed the packet.
326*4882a593Smuzhiyun * @BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK:
327*4882a593Smuzhiyun * firmware tossed the packet after retries.
328*4882a593Smuzhiyun * @BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED:
329*4882a593Smuzhiyun * firmware wrongly reported suppressed previously, now fixing to acked.
330*4882a593Smuzhiyun * @BRCMF_FWS_TXSTATUS_HOST_TOSSED:
331*4882a593Smuzhiyun * host tossed the packet.
332*4882a593Smuzhiyun */
333*4882a593Smuzhiyun enum brcmf_fws_txstatus {
334*4882a593Smuzhiyun BRCMF_FWS_TXSTATUS_DISCARD,
335*4882a593Smuzhiyun BRCMF_FWS_TXSTATUS_CORE_SUPPRESS,
336*4882a593Smuzhiyun BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS,
337*4882a593Smuzhiyun BRCMF_FWS_TXSTATUS_FW_TOSSED,
338*4882a593Smuzhiyun BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK,
339*4882a593Smuzhiyun BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED,
340*4882a593Smuzhiyun BRCMF_FWS_TXSTATUS_HOST_TOSSED
341*4882a593Smuzhiyun };
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun enum brcmf_fws_fcmode {
344*4882a593Smuzhiyun BRCMF_FWS_FCMODE_NONE,
345*4882a593Smuzhiyun BRCMF_FWS_FCMODE_IMPLIED_CREDIT,
346*4882a593Smuzhiyun BRCMF_FWS_FCMODE_EXPLICIT_CREDIT
347*4882a593Smuzhiyun };
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun enum brcmf_fws_mac_desc_state {
350*4882a593Smuzhiyun BRCMF_FWS_STATE_OPEN = 1,
351*4882a593Smuzhiyun BRCMF_FWS_STATE_CLOSE
352*4882a593Smuzhiyun };
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun /**
355*4882a593Smuzhiyun * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface
356*4882a593Smuzhiyun *
357*4882a593Smuzhiyun * @name: name of the descriptor.
358*4882a593Smuzhiyun * @occupied: slot is in use.
359*4882a593Smuzhiyun * @mac_handle: handle for mac entry determined by firmware.
360*4882a593Smuzhiyun * @interface_id: interface index.
361*4882a593Smuzhiyun * @state: current state.
362*4882a593Smuzhiyun * @suppressed: mac entry is suppressed.
363*4882a593Smuzhiyun * @generation: generation bit.
364*4882a593Smuzhiyun * @ac_bitmap: ac queue bitmap.
365*4882a593Smuzhiyun * @requested_credit: credits requested by firmware.
366*4882a593Smuzhiyun * @requested_packet: packet requested by firmware.
367*4882a593Smuzhiyun * @ea: ethernet address.
368*4882a593Smuzhiyun * @seq: per-node free-running sequence.
369*4882a593Smuzhiyun * @psq: power-save queue.
370*4882a593Smuzhiyun * @transit_count: packet in transit to firmware.
371*4882a593Smuzhiyun * @suppr_transit_count: suppressed packet in transit to firmware.
372*4882a593Smuzhiyun * @send_tim_signal: if set tim signal will be sent.
373*4882a593Smuzhiyun * @traffic_pending_bmp: traffic pending bitmap.
374*4882a593Smuzhiyun * @traffic_lastreported_bmp: traffic last reported bitmap.
375*4882a593Smuzhiyun */
376*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor {
377*4882a593Smuzhiyun char name[16];
378*4882a593Smuzhiyun u8 occupied;
379*4882a593Smuzhiyun u8 mac_handle;
380*4882a593Smuzhiyun u8 interface_id;
381*4882a593Smuzhiyun u8 state;
382*4882a593Smuzhiyun bool suppressed;
383*4882a593Smuzhiyun u8 generation;
384*4882a593Smuzhiyun u8 ac_bitmap;
385*4882a593Smuzhiyun u8 requested_credit;
386*4882a593Smuzhiyun u8 requested_packet;
387*4882a593Smuzhiyun u8 ea[ETH_ALEN];
388*4882a593Smuzhiyun u8 seq[BRCMF_FWS_FIFO_COUNT];
389*4882a593Smuzhiyun struct pktq psq;
390*4882a593Smuzhiyun int transit_count;
391*4882a593Smuzhiyun int suppr_transit_count;
392*4882a593Smuzhiyun bool send_tim_signal;
393*4882a593Smuzhiyun u8 traffic_pending_bmp;
394*4882a593Smuzhiyun u8 traffic_lastreported_bmp;
395*4882a593Smuzhiyun };
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun #define BRCMF_FWS_HANGER_MAXITEMS 3072
398*4882a593Smuzhiyun #define BRCMF_BORROW_RATIO 3
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun /**
401*4882a593Smuzhiyun * enum brcmf_fws_hanger_item_state - state of hanger item.
402*4882a593Smuzhiyun *
403*4882a593Smuzhiyun * @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use.
404*4882a593Smuzhiyun * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use.
405*4882a593Smuzhiyun * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed.
406*4882a593Smuzhiyun */
407*4882a593Smuzhiyun enum brcmf_fws_hanger_item_state {
408*4882a593Smuzhiyun BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1,
409*4882a593Smuzhiyun BRCMF_FWS_HANGER_ITEM_STATE_INUSE,
410*4882a593Smuzhiyun BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED
411*4882a593Smuzhiyun };
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun /**
415*4882a593Smuzhiyun * struct brcmf_fws_hanger_item - single entry for tx pending packet.
416*4882a593Smuzhiyun *
417*4882a593Smuzhiyun * @state: entry is either free or occupied.
418*4882a593Smuzhiyun * @pkt: packet itself.
419*4882a593Smuzhiyun */
420*4882a593Smuzhiyun struct brcmf_fws_hanger_item {
421*4882a593Smuzhiyun enum brcmf_fws_hanger_item_state state;
422*4882a593Smuzhiyun struct sk_buff *pkt;
423*4882a593Smuzhiyun };
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun /**
426*4882a593Smuzhiyun * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus.
427*4882a593Smuzhiyun *
428*4882a593Smuzhiyun * @pushed: packets pushed to await txstatus.
429*4882a593Smuzhiyun * @popped: packets popped upon handling txstatus.
430*4882a593Smuzhiyun * @failed_to_push: packets that could not be pushed.
431*4882a593Smuzhiyun * @failed_to_pop: packets that could not be popped.
432*4882a593Smuzhiyun * @failed_slotfind: packets for which failed to find an entry.
433*4882a593Smuzhiyun * @slot_pos: last returned item index for a free entry.
434*4882a593Smuzhiyun * @items: array of hanger items.
435*4882a593Smuzhiyun */
436*4882a593Smuzhiyun struct brcmf_fws_hanger {
437*4882a593Smuzhiyun u32 pushed;
438*4882a593Smuzhiyun u32 popped;
439*4882a593Smuzhiyun u32 failed_to_push;
440*4882a593Smuzhiyun u32 failed_to_pop;
441*4882a593Smuzhiyun u32 failed_slotfind;
442*4882a593Smuzhiyun u32 slot_pos;
443*4882a593Smuzhiyun struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS];
444*4882a593Smuzhiyun };
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun struct brcmf_fws_macdesc_table {
447*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE];
448*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS];
449*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor other;
450*4882a593Smuzhiyun };
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun struct brcmf_fws_stats {
453*4882a593Smuzhiyun u32 tlv_parse_failed;
454*4882a593Smuzhiyun u32 tlv_invalid_type;
455*4882a593Smuzhiyun u32 header_only_pkt;
456*4882a593Smuzhiyun u32 header_pulls;
457*4882a593Smuzhiyun u32 pkt2bus;
458*4882a593Smuzhiyun u32 send_pkts[5];
459*4882a593Smuzhiyun u32 requested_sent[5];
460*4882a593Smuzhiyun u32 generic_error;
461*4882a593Smuzhiyun u32 mac_update_failed;
462*4882a593Smuzhiyun u32 mac_ps_update_failed;
463*4882a593Smuzhiyun u32 if_update_failed;
464*4882a593Smuzhiyun u32 packet_request_failed;
465*4882a593Smuzhiyun u32 credit_request_failed;
466*4882a593Smuzhiyun u32 rollback_success;
467*4882a593Smuzhiyun u32 rollback_failed;
468*4882a593Smuzhiyun u32 delayq_full_error;
469*4882a593Smuzhiyun u32 supprq_full_error;
470*4882a593Smuzhiyun u32 txs_indicate;
471*4882a593Smuzhiyun u32 txs_discard;
472*4882a593Smuzhiyun u32 txs_supp_core;
473*4882a593Smuzhiyun u32 txs_supp_ps;
474*4882a593Smuzhiyun u32 txs_tossed;
475*4882a593Smuzhiyun u32 txs_host_tossed;
476*4882a593Smuzhiyun u32 bus_flow_block;
477*4882a593Smuzhiyun u32 fws_flow_block;
478*4882a593Smuzhiyun };
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun struct brcmf_fws_info {
481*4882a593Smuzhiyun struct brcmf_pub *drvr;
482*4882a593Smuzhiyun spinlock_t spinlock;
483*4882a593Smuzhiyun ulong flags;
484*4882a593Smuzhiyun struct brcmf_fws_stats stats;
485*4882a593Smuzhiyun struct brcmf_fws_hanger hanger;
486*4882a593Smuzhiyun enum brcmf_fws_fcmode fcmode;
487*4882a593Smuzhiyun bool fw_signals;
488*4882a593Smuzhiyun bool bcmc_credit_check;
489*4882a593Smuzhiyun struct brcmf_fws_macdesc_table desc;
490*4882a593Smuzhiyun struct workqueue_struct *fws_wq;
491*4882a593Smuzhiyun struct work_struct fws_dequeue_work;
492*4882a593Smuzhiyun u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
493*4882a593Smuzhiyun int fifo_credit[BRCMF_FWS_FIFO_COUNT];
494*4882a593Smuzhiyun int init_fifo_credit[BRCMF_FWS_FIFO_COUNT];
495*4882a593Smuzhiyun int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1]
496*4882a593Smuzhiyun [BRCMF_FWS_FIFO_AC_VO + 1];
497*4882a593Smuzhiyun int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
498*4882a593Smuzhiyun u32 fifo_credit_map;
499*4882a593Smuzhiyun u32 fifo_delay_map;
500*4882a593Smuzhiyun unsigned long borrow_defer_timestamp;
501*4882a593Smuzhiyun bool bus_flow_blocked;
502*4882a593Smuzhiyun bool creditmap_received;
503*4882a593Smuzhiyun u8 mode;
504*4882a593Smuzhiyun bool avoid_queueing;
505*4882a593Smuzhiyun };
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun #define BRCMF_FWS_TLV_DEF(name, id, len) \
508*4882a593Smuzhiyun case BRCMF_FWS_TYPE_ ## name: \
509*4882a593Smuzhiyun return len;
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun /**
512*4882a593Smuzhiyun * brcmf_fws_get_tlv_len() - returns defined length for given tlv id.
513*4882a593Smuzhiyun *
514*4882a593Smuzhiyun * @fws: firmware-signalling information.
515*4882a593Smuzhiyun * @id: identifier of the TLV.
516*4882a593Smuzhiyun *
517*4882a593Smuzhiyun * Return: the specified length for the given TLV; Otherwise -EINVAL.
518*4882a593Smuzhiyun */
brcmf_fws_get_tlv_len(struct brcmf_fws_info * fws,enum brcmf_fws_tlv_type id)519*4882a593Smuzhiyun static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
520*4882a593Smuzhiyun enum brcmf_fws_tlv_type id)
521*4882a593Smuzhiyun {
522*4882a593Smuzhiyun switch (id) {
523*4882a593Smuzhiyun BRCMF_FWS_TLV_DEFLIST
524*4882a593Smuzhiyun default:
525*4882a593Smuzhiyun fws->stats.tlv_invalid_type++;
526*4882a593Smuzhiyun break;
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun return -EINVAL;
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun #undef BRCMF_FWS_TLV_DEF
531*4882a593Smuzhiyun
brcmf_fws_lock(struct brcmf_fws_info * fws)532*4882a593Smuzhiyun static void brcmf_fws_lock(struct brcmf_fws_info *fws)
533*4882a593Smuzhiyun __acquires(&fws->spinlock)
534*4882a593Smuzhiyun {
535*4882a593Smuzhiyun spin_lock_irqsave(&fws->spinlock, fws->flags);
536*4882a593Smuzhiyun }
537*4882a593Smuzhiyun
brcmf_fws_unlock(struct brcmf_fws_info * fws)538*4882a593Smuzhiyun static void brcmf_fws_unlock(struct brcmf_fws_info *fws)
539*4882a593Smuzhiyun __releases(&fws->spinlock)
540*4882a593Smuzhiyun {
541*4882a593Smuzhiyun spin_unlock_irqrestore(&fws->spinlock, fws->flags);
542*4882a593Smuzhiyun }
543*4882a593Smuzhiyun
brcmf_fws_ifidx_match(struct sk_buff * skb,void * arg)544*4882a593Smuzhiyun static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
545*4882a593Smuzhiyun {
546*4882a593Smuzhiyun u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
547*4882a593Smuzhiyun return ifidx == *(int *)arg;
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun
brcmf_fws_hanger_init(struct brcmf_fws_hanger * hanger)550*4882a593Smuzhiyun static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)
551*4882a593Smuzhiyun {
552*4882a593Smuzhiyun int i;
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun memset(hanger, 0, sizeof(*hanger));
555*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
556*4882a593Smuzhiyun hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun
brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger * h)559*4882a593Smuzhiyun static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun u32 i;
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun while (i != h->slot_pos) {
566*4882a593Smuzhiyun if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
567*4882a593Smuzhiyun h->slot_pos = i;
568*4882a593Smuzhiyun goto done;
569*4882a593Smuzhiyun }
570*4882a593Smuzhiyun i++;
571*4882a593Smuzhiyun if (i == BRCMF_FWS_HANGER_MAXITEMS)
572*4882a593Smuzhiyun i = 0;
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun brcmf_err("all slots occupied\n");
575*4882a593Smuzhiyun h->failed_slotfind++;
576*4882a593Smuzhiyun i = BRCMF_FWS_HANGER_MAXITEMS;
577*4882a593Smuzhiyun done:
578*4882a593Smuzhiyun return i;
579*4882a593Smuzhiyun }
580*4882a593Smuzhiyun
brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger * h,struct sk_buff * pkt,u32 slot_id)581*4882a593Smuzhiyun static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
582*4882a593Smuzhiyun struct sk_buff *pkt, u32 slot_id)
583*4882a593Smuzhiyun {
584*4882a593Smuzhiyun if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
585*4882a593Smuzhiyun return -ENOENT;
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
588*4882a593Smuzhiyun brcmf_err("slot is not free\n");
589*4882a593Smuzhiyun h->failed_to_push++;
590*4882a593Smuzhiyun return -EINVAL;
591*4882a593Smuzhiyun }
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE;
594*4882a593Smuzhiyun h->items[slot_id].pkt = pkt;
595*4882a593Smuzhiyun h->pushed++;
596*4882a593Smuzhiyun return 0;
597*4882a593Smuzhiyun }
598*4882a593Smuzhiyun
brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger * h,u32 slot_id,struct sk_buff ** pktout,bool remove_item)599*4882a593Smuzhiyun static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
600*4882a593Smuzhiyun u32 slot_id, struct sk_buff **pktout,
601*4882a593Smuzhiyun bool remove_item)
602*4882a593Smuzhiyun {
603*4882a593Smuzhiyun if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
604*4882a593Smuzhiyun return -ENOENT;
605*4882a593Smuzhiyun
606*4882a593Smuzhiyun if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
607*4882a593Smuzhiyun brcmf_err("entry not in use\n");
608*4882a593Smuzhiyun h->failed_to_pop++;
609*4882a593Smuzhiyun return -EINVAL;
610*4882a593Smuzhiyun }
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun *pktout = h->items[slot_id].pkt;
613*4882a593Smuzhiyun if (remove_item) {
614*4882a593Smuzhiyun h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
615*4882a593Smuzhiyun h->items[slot_id].pkt = NULL;
616*4882a593Smuzhiyun h->popped++;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun return 0;
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun
brcmf_fws_psq_flush(struct brcmf_fws_info * fws,struct pktq * q,int ifidx)621*4882a593Smuzhiyun static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q,
622*4882a593Smuzhiyun int ifidx)
623*4882a593Smuzhiyun {
624*4882a593Smuzhiyun struct brcmf_fws_hanger_item *hi;
625*4882a593Smuzhiyun bool (*matchfn)(struct sk_buff *, void *) = NULL;
626*4882a593Smuzhiyun struct sk_buff *skb;
627*4882a593Smuzhiyun int prec;
628*4882a593Smuzhiyun u32 hslot;
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun if (ifidx != -1)
631*4882a593Smuzhiyun matchfn = brcmf_fws_ifidx_match;
632*4882a593Smuzhiyun for (prec = 0; prec < q->num_prec; prec++) {
633*4882a593Smuzhiyun skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
634*4882a593Smuzhiyun while (skb) {
635*4882a593Smuzhiyun hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
636*4882a593Smuzhiyun hi = &fws->hanger.items[hslot];
637*4882a593Smuzhiyun WARN_ON(skb != hi->pkt);
638*4882a593Smuzhiyun hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
639*4882a593Smuzhiyun brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
640*4882a593Smuzhiyun true);
641*4882a593Smuzhiyun brcmu_pkt_buf_free_skb(skb);
642*4882a593Smuzhiyun skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun }
645*4882a593Smuzhiyun }
646*4882a593Smuzhiyun
brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger * h,u32 slot_id)647*4882a593Smuzhiyun static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
648*4882a593Smuzhiyun u32 slot_id)
649*4882a593Smuzhiyun {
650*4882a593Smuzhiyun if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
651*4882a593Smuzhiyun return -ENOENT;
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
654*4882a593Smuzhiyun brcmf_err("entry not in use\n");
655*4882a593Smuzhiyun return -EINVAL;
656*4882a593Smuzhiyun }
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
659*4882a593Smuzhiyun return 0;
660*4882a593Smuzhiyun }
661*4882a593Smuzhiyun
brcmf_fws_hanger_cleanup(struct brcmf_fws_info * fws,bool (* fn)(struct sk_buff *,void *),int ifidx)662*4882a593Smuzhiyun static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
663*4882a593Smuzhiyun bool (*fn)(struct sk_buff *, void *),
664*4882a593Smuzhiyun int ifidx)
665*4882a593Smuzhiyun {
666*4882a593Smuzhiyun struct brcmf_fws_hanger *h = &fws->hanger;
667*4882a593Smuzhiyun struct sk_buff *skb;
668*4882a593Smuzhiyun int i;
669*4882a593Smuzhiyun enum brcmf_fws_hanger_item_state s;
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(h->items); i++) {
672*4882a593Smuzhiyun s = h->items[i].state;
673*4882a593Smuzhiyun if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
674*4882a593Smuzhiyun s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
675*4882a593Smuzhiyun skb = h->items[i].pkt;
676*4882a593Smuzhiyun if (fn == NULL || fn(skb, &ifidx)) {
677*4882a593Smuzhiyun /* suppress packets freed from psq */
678*4882a593Smuzhiyun if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE)
679*4882a593Smuzhiyun brcmu_pkt_buf_free_skb(skb);
680*4882a593Smuzhiyun h->items[i].state =
681*4882a593Smuzhiyun BRCMF_FWS_HANGER_ITEM_STATE_FREE;
682*4882a593Smuzhiyun }
683*4882a593Smuzhiyun }
684*4882a593Smuzhiyun }
685*4882a593Smuzhiyun }
686*4882a593Smuzhiyun
brcmf_fws_macdesc_set_name(struct brcmf_fws_info * fws,struct brcmf_fws_mac_descriptor * desc)687*4882a593Smuzhiyun static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws,
688*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *desc)
689*4882a593Smuzhiyun {
690*4882a593Smuzhiyun if (desc == &fws->desc.other)
691*4882a593Smuzhiyun strlcpy(desc->name, "MAC-OTHER", sizeof(desc->name));
692*4882a593Smuzhiyun else if (desc->mac_handle)
693*4882a593Smuzhiyun scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d",
694*4882a593Smuzhiyun desc->mac_handle, desc->interface_id);
695*4882a593Smuzhiyun else
696*4882a593Smuzhiyun scnprintf(desc->name, sizeof(desc->name), "MACIF:%d",
697*4882a593Smuzhiyun desc->interface_id);
698*4882a593Smuzhiyun }
699*4882a593Smuzhiyun
brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor * desc,u8 * addr,u8 ifidx)700*4882a593Smuzhiyun static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc,
701*4882a593Smuzhiyun u8 *addr, u8 ifidx)
702*4882a593Smuzhiyun {
703*4882a593Smuzhiyun brcmf_dbg(TRACE,
704*4882a593Smuzhiyun "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx);
705*4882a593Smuzhiyun desc->occupied = 1;
706*4882a593Smuzhiyun desc->state = BRCMF_FWS_STATE_OPEN;
707*4882a593Smuzhiyun desc->requested_credit = 0;
708*4882a593Smuzhiyun desc->requested_packet = 0;
709*4882a593Smuzhiyun /* depending on use may need ifp->bsscfgidx instead */
710*4882a593Smuzhiyun desc->interface_id = ifidx;
711*4882a593Smuzhiyun desc->ac_bitmap = 0xff; /* update this when handling APSD */
712*4882a593Smuzhiyun if (addr)
713*4882a593Smuzhiyun memcpy(&desc->ea[0], addr, ETH_ALEN);
714*4882a593Smuzhiyun }
715*4882a593Smuzhiyun
716*4882a593Smuzhiyun static
brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor * desc)717*4882a593Smuzhiyun void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc)
718*4882a593Smuzhiyun {
719*4882a593Smuzhiyun brcmf_dbg(TRACE,
720*4882a593Smuzhiyun "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id);
721*4882a593Smuzhiyun desc->occupied = 0;
722*4882a593Smuzhiyun desc->state = BRCMF_FWS_STATE_CLOSE;
723*4882a593Smuzhiyun desc->requested_credit = 0;
724*4882a593Smuzhiyun desc->requested_packet = 0;
725*4882a593Smuzhiyun }
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun static struct brcmf_fws_mac_descriptor *
brcmf_fws_macdesc_lookup(struct brcmf_fws_info * fws,u8 * ea)728*4882a593Smuzhiyun brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)
729*4882a593Smuzhiyun {
730*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry;
731*4882a593Smuzhiyun int i;
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun if (ea == NULL)
734*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun entry = &fws->desc.nodes[0];
737*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) {
738*4882a593Smuzhiyun if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN))
739*4882a593Smuzhiyun return entry;
740*4882a593Smuzhiyun entry++;
741*4882a593Smuzhiyun }
742*4882a593Smuzhiyun
743*4882a593Smuzhiyun return ERR_PTR(-ENOENT);
744*4882a593Smuzhiyun }
745*4882a593Smuzhiyun
746*4882a593Smuzhiyun static struct brcmf_fws_mac_descriptor*
brcmf_fws_macdesc_find(struct brcmf_fws_info * fws,struct brcmf_if * ifp,u8 * da)747*4882a593Smuzhiyun brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da)
748*4882a593Smuzhiyun {
749*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry = &fws->desc.other;
750*4882a593Smuzhiyun bool multicast;
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun multicast = is_multicast_ether_addr(da);
753*4882a593Smuzhiyun
754*4882a593Smuzhiyun /* Multicast destination, STA and P2P clients get the interface entry.
755*4882a593Smuzhiyun * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
756*4882a593Smuzhiyun * have their own entry.
757*4882a593Smuzhiyun */
758*4882a593Smuzhiyun if (multicast && ifp->fws_desc) {
759*4882a593Smuzhiyun entry = ifp->fws_desc;
760*4882a593Smuzhiyun goto done;
761*4882a593Smuzhiyun }
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun entry = brcmf_fws_macdesc_lookup(fws, da);
764*4882a593Smuzhiyun if (IS_ERR(entry))
765*4882a593Smuzhiyun entry = ifp->fws_desc;
766*4882a593Smuzhiyun
767*4882a593Smuzhiyun done:
768*4882a593Smuzhiyun return entry;
769*4882a593Smuzhiyun }
770*4882a593Smuzhiyun
brcmf_fws_macdesc_closed(struct brcmf_fws_info * fws,struct brcmf_fws_mac_descriptor * entry,int fifo)771*4882a593Smuzhiyun static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws,
772*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry,
773*4882a593Smuzhiyun int fifo)
774*4882a593Smuzhiyun {
775*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *if_entry;
776*4882a593Smuzhiyun bool closed;
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun /* for unique destination entries the related interface
779*4882a593Smuzhiyun * may be closed.
780*4882a593Smuzhiyun */
781*4882a593Smuzhiyun if (entry->mac_handle) {
782*4882a593Smuzhiyun if_entry = &fws->desc.iface[entry->interface_id];
783*4882a593Smuzhiyun if (if_entry->state == BRCMF_FWS_STATE_CLOSE)
784*4882a593Smuzhiyun return true;
785*4882a593Smuzhiyun }
786*4882a593Smuzhiyun /* an entry is closed when the state is closed and
787*4882a593Smuzhiyun * the firmware did not request anything.
788*4882a593Smuzhiyun */
789*4882a593Smuzhiyun closed = entry->state == BRCMF_FWS_STATE_CLOSE &&
790*4882a593Smuzhiyun !entry->requested_credit && !entry->requested_packet;
791*4882a593Smuzhiyun
792*4882a593Smuzhiyun /* Or firmware does not allow traffic for given fifo */
793*4882a593Smuzhiyun return closed || !(entry->ac_bitmap & BIT(fifo));
794*4882a593Smuzhiyun }
795*4882a593Smuzhiyun
brcmf_fws_macdesc_cleanup(struct brcmf_fws_info * fws,struct brcmf_fws_mac_descriptor * entry,int ifidx)796*4882a593Smuzhiyun static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,
797*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry,
798*4882a593Smuzhiyun int ifidx)
799*4882a593Smuzhiyun {
800*4882a593Smuzhiyun if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
801*4882a593Smuzhiyun brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
802*4882a593Smuzhiyun entry->occupied = !!(entry->psq.len);
803*4882a593Smuzhiyun }
804*4882a593Smuzhiyun }
805*4882a593Smuzhiyun
brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info * fws,bool (* fn)(struct sk_buff *,void *),int ifidx)806*4882a593Smuzhiyun static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,
807*4882a593Smuzhiyun bool (*fn)(struct sk_buff *, void *),
808*4882a593Smuzhiyun int ifidx)
809*4882a593Smuzhiyun {
810*4882a593Smuzhiyun struct brcmf_fws_hanger_item *hi;
811*4882a593Smuzhiyun struct pktq *txq;
812*4882a593Smuzhiyun struct sk_buff *skb;
813*4882a593Smuzhiyun int prec;
814*4882a593Smuzhiyun u32 hslot;
815*4882a593Smuzhiyun
816*4882a593Smuzhiyun txq = brcmf_bus_gettxq(fws->drvr->bus_if);
817*4882a593Smuzhiyun if (IS_ERR(txq)) {
818*4882a593Smuzhiyun brcmf_dbg(TRACE, "no txq to clean up\n");
819*4882a593Smuzhiyun return;
820*4882a593Smuzhiyun }
821*4882a593Smuzhiyun
822*4882a593Smuzhiyun for (prec = 0; prec < txq->num_prec; prec++) {
823*4882a593Smuzhiyun skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
824*4882a593Smuzhiyun while (skb) {
825*4882a593Smuzhiyun hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
826*4882a593Smuzhiyun hi = &fws->hanger.items[hslot];
827*4882a593Smuzhiyun WARN_ON(skb != hi->pkt);
828*4882a593Smuzhiyun hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
829*4882a593Smuzhiyun brcmu_pkt_buf_free_skb(skb);
830*4882a593Smuzhiyun skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
831*4882a593Smuzhiyun }
832*4882a593Smuzhiyun }
833*4882a593Smuzhiyun }
834*4882a593Smuzhiyun
brcmf_fws_cleanup(struct brcmf_fws_info * fws,int ifidx)835*4882a593Smuzhiyun static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
836*4882a593Smuzhiyun {
837*4882a593Smuzhiyun int i;
838*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *table;
839*4882a593Smuzhiyun bool (*matchfn)(struct sk_buff *, void *) = NULL;
840*4882a593Smuzhiyun
841*4882a593Smuzhiyun if (fws == NULL)
842*4882a593Smuzhiyun return;
843*4882a593Smuzhiyun
844*4882a593Smuzhiyun if (ifidx != -1)
845*4882a593Smuzhiyun matchfn = brcmf_fws_ifidx_match;
846*4882a593Smuzhiyun
847*4882a593Smuzhiyun /* cleanup individual nodes */
848*4882a593Smuzhiyun table = &fws->desc.nodes[0];
849*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)
850*4882a593Smuzhiyun brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx);
851*4882a593Smuzhiyun
852*4882a593Smuzhiyun brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx);
853*4882a593Smuzhiyun brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx);
854*4882a593Smuzhiyun brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
855*4882a593Smuzhiyun }
856*4882a593Smuzhiyun
brcmf_fws_hdrpush(struct brcmf_fws_info * fws,struct sk_buff * skb)857*4882a593Smuzhiyun static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
858*4882a593Smuzhiyun {
859*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
860*4882a593Smuzhiyun u8 *wlh;
861*4882a593Smuzhiyun u16 data_offset = 0;
862*4882a593Smuzhiyun u8 fillers;
863*4882a593Smuzhiyun __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
864*4882a593Smuzhiyun __le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq);
865*4882a593Smuzhiyun
866*4882a593Smuzhiyun brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n",
867*4882a593Smuzhiyun entry->name, brcmf_skb_if_flags_get_field(skb, INDEX),
868*4882a593Smuzhiyun (le32_to_cpu(pkttag) >> 8) & 0xffff,
869*4882a593Smuzhiyun brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq);
870*4882a593Smuzhiyun if (entry->send_tim_signal)
871*4882a593Smuzhiyun data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
872*4882a593Smuzhiyun if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
873*4882a593Smuzhiyun data_offset += BRCMF_FWS_TYPE_SEQ_LEN;
874*4882a593Smuzhiyun /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
875*4882a593Smuzhiyun data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
876*4882a593Smuzhiyun fillers = round_up(data_offset, 4) - data_offset;
877*4882a593Smuzhiyun data_offset += fillers;
878*4882a593Smuzhiyun
879*4882a593Smuzhiyun skb_push(skb, data_offset);
880*4882a593Smuzhiyun wlh = skb->data;
881*4882a593Smuzhiyun
882*4882a593Smuzhiyun wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
883*4882a593Smuzhiyun wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
884*4882a593Smuzhiyun memcpy(&wlh[2], &pkttag, sizeof(pkttag));
885*4882a593Smuzhiyun if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
886*4882a593Smuzhiyun wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN;
887*4882a593Smuzhiyun memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq,
888*4882a593Smuzhiyun sizeof(pktseq));
889*4882a593Smuzhiyun }
890*4882a593Smuzhiyun wlh += wlh[1] + 2;
891*4882a593Smuzhiyun
892*4882a593Smuzhiyun if (entry->send_tim_signal) {
893*4882a593Smuzhiyun entry->send_tim_signal = false;
894*4882a593Smuzhiyun wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
895*4882a593Smuzhiyun wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
896*4882a593Smuzhiyun wlh[2] = entry->mac_handle;
897*4882a593Smuzhiyun wlh[3] = entry->traffic_pending_bmp;
898*4882a593Smuzhiyun brcmf_dbg(TRACE, "adding TIM info: handle %d bmp 0x%X\n",
899*4882a593Smuzhiyun entry->mac_handle, entry->traffic_pending_bmp);
900*4882a593Smuzhiyun wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
901*4882a593Smuzhiyun entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
902*4882a593Smuzhiyun }
903*4882a593Smuzhiyun if (fillers)
904*4882a593Smuzhiyun memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
905*4882a593Smuzhiyun
906*4882a593Smuzhiyun return (u8)(data_offset >> 2);
907*4882a593Smuzhiyun }
908*4882a593Smuzhiyun
brcmf_fws_tim_update(struct brcmf_fws_info * fws,struct brcmf_fws_mac_descriptor * entry,int fifo,bool send_immediately)909*4882a593Smuzhiyun static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
910*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry,
911*4882a593Smuzhiyun int fifo, bool send_immediately)
912*4882a593Smuzhiyun {
913*4882a593Smuzhiyun struct sk_buff *skb;
914*4882a593Smuzhiyun struct brcmf_skbuff_cb *skcb;
915*4882a593Smuzhiyun s32 err;
916*4882a593Smuzhiyun u32 len;
917*4882a593Smuzhiyun u8 data_offset;
918*4882a593Smuzhiyun int ifidx;
919*4882a593Smuzhiyun
920*4882a593Smuzhiyun /* check delayedQ and suppressQ in one call using bitmap */
921*4882a593Smuzhiyun if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
922*4882a593Smuzhiyun entry->traffic_pending_bmp &= ~NBITVAL(fifo);
923*4882a593Smuzhiyun else
924*4882a593Smuzhiyun entry->traffic_pending_bmp |= NBITVAL(fifo);
925*4882a593Smuzhiyun
926*4882a593Smuzhiyun entry->send_tim_signal = false;
927*4882a593Smuzhiyun if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)
928*4882a593Smuzhiyun entry->send_tim_signal = true;
929*4882a593Smuzhiyun if (send_immediately && entry->send_tim_signal &&
930*4882a593Smuzhiyun entry->state == BRCMF_FWS_STATE_CLOSE) {
931*4882a593Smuzhiyun /* create a dummy packet and sent that. The traffic */
932*4882a593Smuzhiyun /* bitmap info will automatically be attached to that packet */
933*4882a593Smuzhiyun len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
934*4882a593Smuzhiyun BRCMF_FWS_TYPE_SEQ_LEN +
935*4882a593Smuzhiyun BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
936*4882a593Smuzhiyun 4 + fws->drvr->hdrlen;
937*4882a593Smuzhiyun skb = brcmu_pkt_buf_get_skb(len);
938*4882a593Smuzhiyun if (skb == NULL)
939*4882a593Smuzhiyun return false;
940*4882a593Smuzhiyun skb_pull(skb, len);
941*4882a593Smuzhiyun skcb = brcmf_skbcb(skb);
942*4882a593Smuzhiyun skcb->mac = entry;
943*4882a593Smuzhiyun skcb->state = BRCMF_FWS_SKBSTATE_TIM;
944*4882a593Smuzhiyun skcb->htod = 0;
945*4882a593Smuzhiyun skcb->htod_seq = 0;
946*4882a593Smuzhiyun data_offset = brcmf_fws_hdrpush(fws, skb);
947*4882a593Smuzhiyun ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
948*4882a593Smuzhiyun brcmf_fws_unlock(fws);
949*4882a593Smuzhiyun err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
950*4882a593Smuzhiyun brcmf_fws_lock(fws);
951*4882a593Smuzhiyun if (err)
952*4882a593Smuzhiyun brcmu_pkt_buf_free_skb(skb);
953*4882a593Smuzhiyun return true;
954*4882a593Smuzhiyun }
955*4882a593Smuzhiyun return false;
956*4882a593Smuzhiyun }
957*4882a593Smuzhiyun
958*4882a593Smuzhiyun static void
brcmf_fws_flow_control_check(struct brcmf_fws_info * fws,struct pktq * pq,u8 if_id)959*4882a593Smuzhiyun brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
960*4882a593Smuzhiyun u8 if_id)
961*4882a593Smuzhiyun {
962*4882a593Smuzhiyun struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
963*4882a593Smuzhiyun
964*4882a593Smuzhiyun if (WARN_ON(!ifp))
965*4882a593Smuzhiyun return;
966*4882a593Smuzhiyun
967*4882a593Smuzhiyun if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
968*4882a593Smuzhiyun pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER)
969*4882a593Smuzhiyun brcmf_txflowblock_if(ifp,
970*4882a593Smuzhiyun BRCMF_NETIF_STOP_REASON_FWS_FC, false);
971*4882a593Smuzhiyun if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
972*4882a593Smuzhiyun pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) {
973*4882a593Smuzhiyun fws->stats.fws_flow_block++;
974*4882a593Smuzhiyun brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);
975*4882a593Smuzhiyun }
976*4882a593Smuzhiyun return;
977*4882a593Smuzhiyun }
978*4882a593Smuzhiyun
brcmf_fws_rssi_indicate(struct brcmf_fws_info * fws,s8 rssi)979*4882a593Smuzhiyun static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
980*4882a593Smuzhiyun {
981*4882a593Smuzhiyun brcmf_dbg(CTL, "rssi %d\n", rssi);
982*4882a593Smuzhiyun return 0;
983*4882a593Smuzhiyun }
984*4882a593Smuzhiyun
985*4882a593Smuzhiyun static
brcmf_fws_macdesc_indicate(struct brcmf_fws_info * fws,u8 type,u8 * data)986*4882a593Smuzhiyun int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
987*4882a593Smuzhiyun {
988*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry, *existing;
989*4882a593Smuzhiyun u8 mac_handle;
990*4882a593Smuzhiyun u8 ifidx;
991*4882a593Smuzhiyun u8 *addr;
992*4882a593Smuzhiyun
993*4882a593Smuzhiyun mac_handle = *data++;
994*4882a593Smuzhiyun ifidx = *data++;
995*4882a593Smuzhiyun addr = data;
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun entry = &fws->desc.nodes[mac_handle & 0x1F];
998*4882a593Smuzhiyun if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
999*4882a593Smuzhiyun if (entry->occupied) {
1000*4882a593Smuzhiyun brcmf_dbg(TRACE, "deleting %s mac %pM\n",
1001*4882a593Smuzhiyun entry->name, addr);
1002*4882a593Smuzhiyun brcmf_fws_lock(fws);
1003*4882a593Smuzhiyun brcmf_fws_macdesc_cleanup(fws, entry, -1);
1004*4882a593Smuzhiyun brcmf_fws_macdesc_deinit(entry);
1005*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1006*4882a593Smuzhiyun } else
1007*4882a593Smuzhiyun fws->stats.mac_update_failed++;
1008*4882a593Smuzhiyun return 0;
1009*4882a593Smuzhiyun }
1010*4882a593Smuzhiyun
1011*4882a593Smuzhiyun existing = brcmf_fws_macdesc_lookup(fws, addr);
1012*4882a593Smuzhiyun if (IS_ERR(existing)) {
1013*4882a593Smuzhiyun if (!entry->occupied) {
1014*4882a593Smuzhiyun brcmf_fws_lock(fws);
1015*4882a593Smuzhiyun entry->mac_handle = mac_handle;
1016*4882a593Smuzhiyun brcmf_fws_macdesc_init(entry, addr, ifidx);
1017*4882a593Smuzhiyun brcmf_fws_macdesc_set_name(fws, entry);
1018*4882a593Smuzhiyun brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
1019*4882a593Smuzhiyun BRCMF_FWS_PSQ_LEN);
1020*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1021*4882a593Smuzhiyun brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
1022*4882a593Smuzhiyun } else {
1023*4882a593Smuzhiyun fws->stats.mac_update_failed++;
1024*4882a593Smuzhiyun }
1025*4882a593Smuzhiyun } else {
1026*4882a593Smuzhiyun if (entry != existing) {
1027*4882a593Smuzhiyun brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
1028*4882a593Smuzhiyun brcmf_fws_lock(fws);
1029*4882a593Smuzhiyun memcpy(entry, existing,
1030*4882a593Smuzhiyun offsetof(struct brcmf_fws_mac_descriptor, psq));
1031*4882a593Smuzhiyun entry->mac_handle = mac_handle;
1032*4882a593Smuzhiyun brcmf_fws_macdesc_deinit(existing);
1033*4882a593Smuzhiyun brcmf_fws_macdesc_set_name(fws, entry);
1034*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1035*4882a593Smuzhiyun brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
1036*4882a593Smuzhiyun addr);
1037*4882a593Smuzhiyun } else {
1038*4882a593Smuzhiyun brcmf_dbg(TRACE, "use existing\n");
1039*4882a593Smuzhiyun WARN_ON(entry->mac_handle != mac_handle);
1040*4882a593Smuzhiyun /* TODO: what should we do here: continue, reinit, .. */
1041*4882a593Smuzhiyun }
1042*4882a593Smuzhiyun }
1043*4882a593Smuzhiyun return 0;
1044*4882a593Smuzhiyun }
1045*4882a593Smuzhiyun
brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info * fws,u8 type,u8 * data)1046*4882a593Smuzhiyun static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
1047*4882a593Smuzhiyun u8 type, u8 *data)
1048*4882a593Smuzhiyun {
1049*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry;
1050*4882a593Smuzhiyun u8 mac_handle;
1051*4882a593Smuzhiyun int ret;
1052*4882a593Smuzhiyun
1053*4882a593Smuzhiyun mac_handle = data[0];
1054*4882a593Smuzhiyun entry = &fws->desc.nodes[mac_handle & 0x1F];
1055*4882a593Smuzhiyun if (!entry->occupied) {
1056*4882a593Smuzhiyun fws->stats.mac_ps_update_failed++;
1057*4882a593Smuzhiyun return -ESRCH;
1058*4882a593Smuzhiyun }
1059*4882a593Smuzhiyun brcmf_fws_lock(fws);
1060*4882a593Smuzhiyun /* a state update should wipe old credits */
1061*4882a593Smuzhiyun entry->requested_credit = 0;
1062*4882a593Smuzhiyun entry->requested_packet = 0;
1063*4882a593Smuzhiyun if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
1064*4882a593Smuzhiyun entry->state = BRCMF_FWS_STATE_OPEN;
1065*4882a593Smuzhiyun ret = BRCMF_FWS_RET_OK_SCHEDULE;
1066*4882a593Smuzhiyun } else {
1067*4882a593Smuzhiyun entry->state = BRCMF_FWS_STATE_CLOSE;
1068*4882a593Smuzhiyun brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false);
1069*4882a593Smuzhiyun brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false);
1070*4882a593Smuzhiyun brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false);
1071*4882a593Smuzhiyun brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
1072*4882a593Smuzhiyun ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
1073*4882a593Smuzhiyun }
1074*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1075*4882a593Smuzhiyun return ret;
1076*4882a593Smuzhiyun }
1077*4882a593Smuzhiyun
brcmf_fws_interface_state_indicate(struct brcmf_fws_info * fws,u8 type,u8 * data)1078*4882a593Smuzhiyun static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
1079*4882a593Smuzhiyun u8 type, u8 *data)
1080*4882a593Smuzhiyun {
1081*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry;
1082*4882a593Smuzhiyun u8 ifidx;
1083*4882a593Smuzhiyun int ret;
1084*4882a593Smuzhiyun
1085*4882a593Smuzhiyun ifidx = data[0];
1086*4882a593Smuzhiyun
1087*4882a593Smuzhiyun if (ifidx >= BRCMF_MAX_IFS) {
1088*4882a593Smuzhiyun ret = -ERANGE;
1089*4882a593Smuzhiyun goto fail;
1090*4882a593Smuzhiyun }
1091*4882a593Smuzhiyun
1092*4882a593Smuzhiyun entry = &fws->desc.iface[ifidx];
1093*4882a593Smuzhiyun if (!entry->occupied) {
1094*4882a593Smuzhiyun ret = -ESRCH;
1095*4882a593Smuzhiyun goto fail;
1096*4882a593Smuzhiyun }
1097*4882a593Smuzhiyun
1098*4882a593Smuzhiyun brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
1099*4882a593Smuzhiyun entry->name);
1100*4882a593Smuzhiyun brcmf_fws_lock(fws);
1101*4882a593Smuzhiyun switch (type) {
1102*4882a593Smuzhiyun case BRCMF_FWS_TYPE_INTERFACE_OPEN:
1103*4882a593Smuzhiyun entry->state = BRCMF_FWS_STATE_OPEN;
1104*4882a593Smuzhiyun ret = BRCMF_FWS_RET_OK_SCHEDULE;
1105*4882a593Smuzhiyun break;
1106*4882a593Smuzhiyun case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
1107*4882a593Smuzhiyun entry->state = BRCMF_FWS_STATE_CLOSE;
1108*4882a593Smuzhiyun ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
1109*4882a593Smuzhiyun break;
1110*4882a593Smuzhiyun default:
1111*4882a593Smuzhiyun ret = -EINVAL;
1112*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1113*4882a593Smuzhiyun goto fail;
1114*4882a593Smuzhiyun }
1115*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1116*4882a593Smuzhiyun return ret;
1117*4882a593Smuzhiyun
1118*4882a593Smuzhiyun fail:
1119*4882a593Smuzhiyun fws->stats.if_update_failed++;
1120*4882a593Smuzhiyun return ret;
1121*4882a593Smuzhiyun }
1122*4882a593Smuzhiyun
brcmf_fws_request_indicate(struct brcmf_fws_info * fws,u8 type,u8 * data)1123*4882a593Smuzhiyun static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
1124*4882a593Smuzhiyun u8 *data)
1125*4882a593Smuzhiyun {
1126*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry;
1127*4882a593Smuzhiyun
1128*4882a593Smuzhiyun entry = &fws->desc.nodes[data[1] & 0x1F];
1129*4882a593Smuzhiyun if (!entry->occupied) {
1130*4882a593Smuzhiyun if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
1131*4882a593Smuzhiyun fws->stats.credit_request_failed++;
1132*4882a593Smuzhiyun else
1133*4882a593Smuzhiyun fws->stats.packet_request_failed++;
1134*4882a593Smuzhiyun return -ESRCH;
1135*4882a593Smuzhiyun }
1136*4882a593Smuzhiyun
1137*4882a593Smuzhiyun brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
1138*4882a593Smuzhiyun brcmf_fws_get_tlv_name(type), type, entry->name,
1139*4882a593Smuzhiyun data[0], data[2]);
1140*4882a593Smuzhiyun brcmf_fws_lock(fws);
1141*4882a593Smuzhiyun if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
1142*4882a593Smuzhiyun entry->requested_credit = data[0];
1143*4882a593Smuzhiyun else
1144*4882a593Smuzhiyun entry->requested_packet = data[0];
1145*4882a593Smuzhiyun
1146*4882a593Smuzhiyun entry->ac_bitmap = data[2];
1147*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1148*4882a593Smuzhiyun return BRCMF_FWS_RET_OK_SCHEDULE;
1149*4882a593Smuzhiyun }
1150*4882a593Smuzhiyun
1151*4882a593Smuzhiyun static void
brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor * entry,struct sk_buff * skb)1152*4882a593Smuzhiyun brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry,
1153*4882a593Smuzhiyun struct sk_buff *skb)
1154*4882a593Smuzhiyun {
1155*4882a593Smuzhiyun if (entry->requested_credit > 0) {
1156*4882a593Smuzhiyun entry->requested_credit--;
1157*4882a593Smuzhiyun brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
1158*4882a593Smuzhiyun brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1);
1159*4882a593Smuzhiyun if (entry->state != BRCMF_FWS_STATE_CLOSE)
1160*4882a593Smuzhiyun brcmf_err("requested credit set while mac not closed!\n");
1161*4882a593Smuzhiyun } else if (entry->requested_packet > 0) {
1162*4882a593Smuzhiyun entry->requested_packet--;
1163*4882a593Smuzhiyun brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
1164*4882a593Smuzhiyun brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
1165*4882a593Smuzhiyun if (entry->state != BRCMF_FWS_STATE_CLOSE)
1166*4882a593Smuzhiyun brcmf_err("requested packet set while mac not closed!\n");
1167*4882a593Smuzhiyun } else {
1168*4882a593Smuzhiyun brcmf_skb_if_flags_set_field(skb, REQUESTED, 0);
1169*4882a593Smuzhiyun brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
1170*4882a593Smuzhiyun }
1171*4882a593Smuzhiyun }
1172*4882a593Smuzhiyun
brcmf_fws_macdesc_return_req_credit(struct sk_buff * skb)1173*4882a593Smuzhiyun static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb)
1174*4882a593Smuzhiyun {
1175*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
1176*4882a593Smuzhiyun
1177*4882a593Smuzhiyun if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) &&
1178*4882a593Smuzhiyun (entry->state == BRCMF_FWS_STATE_CLOSE))
1179*4882a593Smuzhiyun entry->requested_credit++;
1180*4882a593Smuzhiyun }
1181*4882a593Smuzhiyun
brcmf_fws_return_credits(struct brcmf_fws_info * fws,u8 fifo,u8 credits)1182*4882a593Smuzhiyun static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
1183*4882a593Smuzhiyun u8 fifo, u8 credits)
1184*4882a593Smuzhiyun {
1185*4882a593Smuzhiyun int lender_ac;
1186*4882a593Smuzhiyun int *borrowed;
1187*4882a593Smuzhiyun int *fifo_credit;
1188*4882a593Smuzhiyun
1189*4882a593Smuzhiyun if (!credits)
1190*4882a593Smuzhiyun return;
1191*4882a593Smuzhiyun
1192*4882a593Smuzhiyun fws->fifo_credit_map |= 1 << fifo;
1193*4882a593Smuzhiyun
1194*4882a593Smuzhiyun if (fifo > BRCMF_FWS_FIFO_AC_BK &&
1195*4882a593Smuzhiyun fifo <= BRCMF_FWS_FIFO_AC_VO) {
1196*4882a593Smuzhiyun for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
1197*4882a593Smuzhiyun lender_ac--) {
1198*4882a593Smuzhiyun borrowed = &fws->credits_borrowed[fifo][lender_ac];
1199*4882a593Smuzhiyun if (*borrowed) {
1200*4882a593Smuzhiyun fws->fifo_credit_map |= (1 << lender_ac);
1201*4882a593Smuzhiyun fifo_credit = &fws->fifo_credit[lender_ac];
1202*4882a593Smuzhiyun if (*borrowed >= credits) {
1203*4882a593Smuzhiyun *borrowed -= credits;
1204*4882a593Smuzhiyun *fifo_credit += credits;
1205*4882a593Smuzhiyun return;
1206*4882a593Smuzhiyun } else {
1207*4882a593Smuzhiyun credits -= *borrowed;
1208*4882a593Smuzhiyun *fifo_credit += *borrowed;
1209*4882a593Smuzhiyun *borrowed = 0;
1210*4882a593Smuzhiyun }
1211*4882a593Smuzhiyun }
1212*4882a593Smuzhiyun }
1213*4882a593Smuzhiyun }
1214*4882a593Smuzhiyun
1215*4882a593Smuzhiyun if (credits) {
1216*4882a593Smuzhiyun fws->fifo_credit[fifo] += credits;
1217*4882a593Smuzhiyun }
1218*4882a593Smuzhiyun
1219*4882a593Smuzhiyun if (fws->fifo_credit[fifo] > fws->init_fifo_credit[fifo])
1220*4882a593Smuzhiyun fws->fifo_credit[fifo] = fws->init_fifo_credit[fifo];
1221*4882a593Smuzhiyun
1222*4882a593Smuzhiyun }
1223*4882a593Smuzhiyun
brcmf_fws_schedule_deq(struct brcmf_fws_info * fws)1224*4882a593Smuzhiyun static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
1225*4882a593Smuzhiyun {
1226*4882a593Smuzhiyun /* only schedule dequeue when there are credits for delayed traffic */
1227*4882a593Smuzhiyun if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
1228*4882a593Smuzhiyun (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
1229*4882a593Smuzhiyun queue_work(fws->fws_wq, &fws->fws_dequeue_work);
1230*4882a593Smuzhiyun }
1231*4882a593Smuzhiyun
brcmf_fws_enq(struct brcmf_fws_info * fws,enum brcmf_fws_skb_state state,int fifo,struct sk_buff * p)1232*4882a593Smuzhiyun static int brcmf_fws_enq(struct brcmf_fws_info *fws,
1233*4882a593Smuzhiyun enum brcmf_fws_skb_state state, int fifo,
1234*4882a593Smuzhiyun struct sk_buff *p)
1235*4882a593Smuzhiyun {
1236*4882a593Smuzhiyun struct brcmf_pub *drvr = fws->drvr;
1237*4882a593Smuzhiyun int prec = 2 * fifo;
1238*4882a593Smuzhiyun u32 *qfull_stat = &fws->stats.delayq_full_error;
1239*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry;
1240*4882a593Smuzhiyun struct pktq *pq;
1241*4882a593Smuzhiyun struct sk_buff_head *queue;
1242*4882a593Smuzhiyun struct sk_buff *p_head;
1243*4882a593Smuzhiyun struct sk_buff *p_tail;
1244*4882a593Smuzhiyun u32 fr_new;
1245*4882a593Smuzhiyun u32 fr_compare;
1246*4882a593Smuzhiyun
1247*4882a593Smuzhiyun entry = brcmf_skbcb(p)->mac;
1248*4882a593Smuzhiyun if (entry == NULL) {
1249*4882a593Smuzhiyun bphy_err(drvr, "no mac descriptor found for skb %p\n", p);
1250*4882a593Smuzhiyun return -ENOENT;
1251*4882a593Smuzhiyun }
1252*4882a593Smuzhiyun
1253*4882a593Smuzhiyun brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p);
1254*4882a593Smuzhiyun if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
1255*4882a593Smuzhiyun prec += 1;
1256*4882a593Smuzhiyun qfull_stat = &fws->stats.supprq_full_error;
1257*4882a593Smuzhiyun
1258*4882a593Smuzhiyun /* Fix out of order delivery of frames. Dont assume frame */
1259*4882a593Smuzhiyun /* can be inserted at the end, but look for correct position */
1260*4882a593Smuzhiyun pq = &entry->psq;
1261*4882a593Smuzhiyun if (pktq_full(pq) || pktq_pfull(pq, prec)) {
1262*4882a593Smuzhiyun *qfull_stat += 1;
1263*4882a593Smuzhiyun return -ENFILE;
1264*4882a593Smuzhiyun }
1265*4882a593Smuzhiyun queue = &pq->q[prec].skblist;
1266*4882a593Smuzhiyun
1267*4882a593Smuzhiyun p_head = skb_peek(queue);
1268*4882a593Smuzhiyun p_tail = skb_peek_tail(queue);
1269*4882a593Smuzhiyun fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN);
1270*4882a593Smuzhiyun
1271*4882a593Smuzhiyun while (p_head != p_tail) {
1272*4882a593Smuzhiyun fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
1273*4882a593Smuzhiyun FREERUN);
1274*4882a593Smuzhiyun /* be sure to handle wrap of 256 */
1275*4882a593Smuzhiyun if (((fr_new > fr_compare) &&
1276*4882a593Smuzhiyun ((fr_new - fr_compare) < 128)) ||
1277*4882a593Smuzhiyun ((fr_new < fr_compare) &&
1278*4882a593Smuzhiyun ((fr_compare - fr_new) > 128)))
1279*4882a593Smuzhiyun break;
1280*4882a593Smuzhiyun p_tail = skb_queue_prev(queue, p_tail);
1281*4882a593Smuzhiyun }
1282*4882a593Smuzhiyun /* Position found. Determine what to do */
1283*4882a593Smuzhiyun if (p_tail == NULL) {
1284*4882a593Smuzhiyun /* empty list */
1285*4882a593Smuzhiyun __skb_queue_tail(queue, p);
1286*4882a593Smuzhiyun } else {
1287*4882a593Smuzhiyun fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
1288*4882a593Smuzhiyun FREERUN);
1289*4882a593Smuzhiyun if (((fr_new > fr_compare) &&
1290*4882a593Smuzhiyun ((fr_new - fr_compare) < 128)) ||
1291*4882a593Smuzhiyun ((fr_new < fr_compare) &&
1292*4882a593Smuzhiyun ((fr_compare - fr_new) > 128))) {
1293*4882a593Smuzhiyun /* After tail */
1294*4882a593Smuzhiyun __skb_queue_after(queue, p_tail, p);
1295*4882a593Smuzhiyun } else {
1296*4882a593Smuzhiyun /* Before tail */
1297*4882a593Smuzhiyun __skb_insert(p, p_tail->prev, p_tail, queue);
1298*4882a593Smuzhiyun }
1299*4882a593Smuzhiyun }
1300*4882a593Smuzhiyun
1301*4882a593Smuzhiyun /* Complete the counters and statistics */
1302*4882a593Smuzhiyun pq->len++;
1303*4882a593Smuzhiyun if (pq->hi_prec < prec)
1304*4882a593Smuzhiyun pq->hi_prec = (u8) prec;
1305*4882a593Smuzhiyun } else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) {
1306*4882a593Smuzhiyun *qfull_stat += 1;
1307*4882a593Smuzhiyun return -ENFILE;
1308*4882a593Smuzhiyun }
1309*4882a593Smuzhiyun
1310*4882a593Smuzhiyun /* increment total enqueued packet count */
1311*4882a593Smuzhiyun fws->fifo_delay_map |= 1 << fifo;
1312*4882a593Smuzhiyun fws->fifo_enqpkt[fifo]++;
1313*4882a593Smuzhiyun
1314*4882a593Smuzhiyun /* update the sk_buff state */
1315*4882a593Smuzhiyun brcmf_skbcb(p)->state = state;
1316*4882a593Smuzhiyun
1317*4882a593Smuzhiyun /*
1318*4882a593Smuzhiyun * A packet has been pushed so update traffic
1319*4882a593Smuzhiyun * availability bitmap, if applicable
1320*4882a593Smuzhiyun */
1321*4882a593Smuzhiyun brcmf_fws_tim_update(fws, entry, fifo, true);
1322*4882a593Smuzhiyun brcmf_fws_flow_control_check(fws, &entry->psq,
1323*4882a593Smuzhiyun brcmf_skb_if_flags_get_field(p, INDEX));
1324*4882a593Smuzhiyun return 0;
1325*4882a593Smuzhiyun }
1326*4882a593Smuzhiyun
brcmf_fws_deq(struct brcmf_fws_info * fws,int fifo)1327*4882a593Smuzhiyun static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
1328*4882a593Smuzhiyun {
1329*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *table;
1330*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry;
1331*4882a593Smuzhiyun struct sk_buff *p;
1332*4882a593Smuzhiyun int num_nodes;
1333*4882a593Smuzhiyun int node_pos;
1334*4882a593Smuzhiyun int prec_out;
1335*4882a593Smuzhiyun int pmsk;
1336*4882a593Smuzhiyun int i;
1337*4882a593Smuzhiyun
1338*4882a593Smuzhiyun table = (struct brcmf_fws_mac_descriptor *)&fws->desc;
1339*4882a593Smuzhiyun num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor);
1340*4882a593Smuzhiyun node_pos = fws->deq_node_pos[fifo];
1341*4882a593Smuzhiyun
1342*4882a593Smuzhiyun for (i = 0; i < num_nodes; i++) {
1343*4882a593Smuzhiyun entry = &table[(node_pos + i) % num_nodes];
1344*4882a593Smuzhiyun if (!entry->occupied ||
1345*4882a593Smuzhiyun brcmf_fws_macdesc_closed(fws, entry, fifo))
1346*4882a593Smuzhiyun continue;
1347*4882a593Smuzhiyun
1348*4882a593Smuzhiyun if (entry->suppressed)
1349*4882a593Smuzhiyun pmsk = 2;
1350*4882a593Smuzhiyun else
1351*4882a593Smuzhiyun pmsk = 3;
1352*4882a593Smuzhiyun p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
1353*4882a593Smuzhiyun if (p == NULL) {
1354*4882a593Smuzhiyun if (entry->suppressed) {
1355*4882a593Smuzhiyun if (entry->suppr_transit_count)
1356*4882a593Smuzhiyun continue;
1357*4882a593Smuzhiyun entry->suppressed = false;
1358*4882a593Smuzhiyun p = brcmu_pktq_mdeq(&entry->psq,
1359*4882a593Smuzhiyun 1 << (fifo * 2), &prec_out);
1360*4882a593Smuzhiyun }
1361*4882a593Smuzhiyun }
1362*4882a593Smuzhiyun if (p == NULL)
1363*4882a593Smuzhiyun continue;
1364*4882a593Smuzhiyun
1365*4882a593Smuzhiyun brcmf_fws_macdesc_use_req_credit(entry, p);
1366*4882a593Smuzhiyun
1367*4882a593Smuzhiyun /* move dequeue position to ensure fair round-robin */
1368*4882a593Smuzhiyun fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes;
1369*4882a593Smuzhiyun brcmf_fws_flow_control_check(fws, &entry->psq,
1370*4882a593Smuzhiyun brcmf_skb_if_flags_get_field(p,
1371*4882a593Smuzhiyun INDEX)
1372*4882a593Smuzhiyun );
1373*4882a593Smuzhiyun /*
1374*4882a593Smuzhiyun * A packet has been picked up, update traffic
1375*4882a593Smuzhiyun * availability bitmap, if applicable
1376*4882a593Smuzhiyun */
1377*4882a593Smuzhiyun brcmf_fws_tim_update(fws, entry, fifo, false);
1378*4882a593Smuzhiyun
1379*4882a593Smuzhiyun /*
1380*4882a593Smuzhiyun * decrement total enqueued fifo packets and
1381*4882a593Smuzhiyun * clear delay bitmap if done.
1382*4882a593Smuzhiyun */
1383*4882a593Smuzhiyun fws->fifo_enqpkt[fifo]--;
1384*4882a593Smuzhiyun if (fws->fifo_enqpkt[fifo] == 0)
1385*4882a593Smuzhiyun fws->fifo_delay_map &= ~(1 << fifo);
1386*4882a593Smuzhiyun goto done;
1387*4882a593Smuzhiyun }
1388*4882a593Smuzhiyun p = NULL;
1389*4882a593Smuzhiyun done:
1390*4882a593Smuzhiyun brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);
1391*4882a593Smuzhiyun return p;
1392*4882a593Smuzhiyun }
1393*4882a593Smuzhiyun
brcmf_fws_txstatus_suppressed(struct brcmf_fws_info * fws,int fifo,struct sk_buff * skb,u32 genbit,u16 seq)1394*4882a593Smuzhiyun static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
1395*4882a593Smuzhiyun struct sk_buff *skb,
1396*4882a593Smuzhiyun u32 genbit, u16 seq)
1397*4882a593Smuzhiyun {
1398*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
1399*4882a593Smuzhiyun u32 hslot;
1400*4882a593Smuzhiyun int ret;
1401*4882a593Smuzhiyun
1402*4882a593Smuzhiyun hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
1403*4882a593Smuzhiyun
1404*4882a593Smuzhiyun /* this packet was suppressed */
1405*4882a593Smuzhiyun if (!entry->suppressed) {
1406*4882a593Smuzhiyun entry->suppressed = true;
1407*4882a593Smuzhiyun entry->suppr_transit_count = entry->transit_count;
1408*4882a593Smuzhiyun brcmf_dbg(DATA, "suppress %s: transit %d\n",
1409*4882a593Smuzhiyun entry->name, entry->transit_count);
1410*4882a593Smuzhiyun }
1411*4882a593Smuzhiyun
1412*4882a593Smuzhiyun entry->generation = genbit;
1413*4882a593Smuzhiyun
1414*4882a593Smuzhiyun brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
1415*4882a593Smuzhiyun brcmf_skbcb(skb)->htod_seq = seq;
1416*4882a593Smuzhiyun if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
1417*4882a593Smuzhiyun brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
1418*4882a593Smuzhiyun brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
1419*4882a593Smuzhiyun } else {
1420*4882a593Smuzhiyun brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
1421*4882a593Smuzhiyun }
1422*4882a593Smuzhiyun ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
1423*4882a593Smuzhiyun
1424*4882a593Smuzhiyun if (ret != 0) {
1425*4882a593Smuzhiyun /* suppress q is full drop this packet */
1426*4882a593Smuzhiyun brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true);
1427*4882a593Smuzhiyun } else {
1428*4882a593Smuzhiyun /* Mark suppressed to avoid a double free during wlfc cleanup */
1429*4882a593Smuzhiyun brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
1430*4882a593Smuzhiyun }
1431*4882a593Smuzhiyun
1432*4882a593Smuzhiyun return ret;
1433*4882a593Smuzhiyun }
1434*4882a593Smuzhiyun
1435*4882a593Smuzhiyun static int
brcmf_fws_txs_process(struct brcmf_fws_info * fws,u8 flags,u32 hslot,u32 genbit,u16 seq,u8 compcnt)1436*4882a593Smuzhiyun brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
1437*4882a593Smuzhiyun u32 genbit, u16 seq, u8 compcnt)
1438*4882a593Smuzhiyun {
1439*4882a593Smuzhiyun struct brcmf_pub *drvr = fws->drvr;
1440*4882a593Smuzhiyun u32 fifo;
1441*4882a593Smuzhiyun u8 cnt = 0;
1442*4882a593Smuzhiyun int ret;
1443*4882a593Smuzhiyun bool remove_from_hanger = true;
1444*4882a593Smuzhiyun struct sk_buff *skb;
1445*4882a593Smuzhiyun struct brcmf_skbuff_cb *skcb;
1446*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry = NULL;
1447*4882a593Smuzhiyun struct brcmf_if *ifp;
1448*4882a593Smuzhiyun
1449*4882a593Smuzhiyun brcmf_dbg(DATA, "flags %d\n", flags);
1450*4882a593Smuzhiyun
1451*4882a593Smuzhiyun if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
1452*4882a593Smuzhiyun fws->stats.txs_discard += compcnt;
1453*4882a593Smuzhiyun else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) {
1454*4882a593Smuzhiyun fws->stats.txs_supp_core += compcnt;
1455*4882a593Smuzhiyun remove_from_hanger = false;
1456*4882a593Smuzhiyun } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) {
1457*4882a593Smuzhiyun fws->stats.txs_supp_ps += compcnt;
1458*4882a593Smuzhiyun remove_from_hanger = false;
1459*4882a593Smuzhiyun } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)
1460*4882a593Smuzhiyun fws->stats.txs_tossed += compcnt;
1461*4882a593Smuzhiyun else if (flags == BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK)
1462*4882a593Smuzhiyun fws->stats.txs_discard += compcnt;
1463*4882a593Smuzhiyun else if (flags == BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED)
1464*4882a593Smuzhiyun fws->stats.txs_discard += compcnt;
1465*4882a593Smuzhiyun else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)
1466*4882a593Smuzhiyun fws->stats.txs_host_tossed += compcnt;
1467*4882a593Smuzhiyun else
1468*4882a593Smuzhiyun bphy_err(drvr, "unexpected txstatus\n");
1469*4882a593Smuzhiyun
1470*4882a593Smuzhiyun while (cnt < compcnt) {
1471*4882a593Smuzhiyun ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
1472*4882a593Smuzhiyun remove_from_hanger);
1473*4882a593Smuzhiyun if (ret != 0) {
1474*4882a593Smuzhiyun bphy_err(drvr, "no packet in hanger slot: hslot=%d\n",
1475*4882a593Smuzhiyun hslot);
1476*4882a593Smuzhiyun goto cont;
1477*4882a593Smuzhiyun }
1478*4882a593Smuzhiyun
1479*4882a593Smuzhiyun skcb = brcmf_skbcb(skb);
1480*4882a593Smuzhiyun entry = skcb->mac;
1481*4882a593Smuzhiyun if (WARN_ON(!entry)) {
1482*4882a593Smuzhiyun brcmu_pkt_buf_free_skb(skb);
1483*4882a593Smuzhiyun goto cont;
1484*4882a593Smuzhiyun }
1485*4882a593Smuzhiyun entry->transit_count--;
1486*4882a593Smuzhiyun if (entry->suppressed && entry->suppr_transit_count)
1487*4882a593Smuzhiyun entry->suppr_transit_count--;
1488*4882a593Smuzhiyun
1489*4882a593Smuzhiyun brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name,
1490*4882a593Smuzhiyun flags, skcb->htod, seq);
1491*4882a593Smuzhiyun
1492*4882a593Smuzhiyun /* pick up the implicit credit from this packet */
1493*4882a593Smuzhiyun fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
1494*4882a593Smuzhiyun if (fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT ||
1495*4882a593Smuzhiyun (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
1496*4882a593Smuzhiyun flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED) {
1497*4882a593Smuzhiyun brcmf_fws_return_credits(fws, fifo, 1);
1498*4882a593Smuzhiyun brcmf_fws_schedule_deq(fws);
1499*4882a593Smuzhiyun }
1500*4882a593Smuzhiyun brcmf_fws_macdesc_return_req_credit(skb);
1501*4882a593Smuzhiyun
1502*4882a593Smuzhiyun ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
1503*4882a593Smuzhiyun if (ret) {
1504*4882a593Smuzhiyun brcmu_pkt_buf_free_skb(skb);
1505*4882a593Smuzhiyun goto cont;
1506*4882a593Smuzhiyun }
1507*4882a593Smuzhiyun if (!remove_from_hanger)
1508*4882a593Smuzhiyun ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
1509*4882a593Smuzhiyun genbit, seq);
1510*4882a593Smuzhiyun if (remove_from_hanger || ret)
1511*4882a593Smuzhiyun brcmf_txfinalize(ifp, skb, true);
1512*4882a593Smuzhiyun
1513*4882a593Smuzhiyun cont:
1514*4882a593Smuzhiyun hslot = (hslot + 1) & (BRCMF_FWS_TXSTAT_HSLOT_MASK >>
1515*4882a593Smuzhiyun BRCMF_FWS_TXSTAT_HSLOT_SHIFT);
1516*4882a593Smuzhiyun if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
1517*4882a593Smuzhiyun seq = (seq + 1) & BRCMF_SKB_HTOD_SEQ_NR_MASK;
1518*4882a593Smuzhiyun
1519*4882a593Smuzhiyun cnt++;
1520*4882a593Smuzhiyun }
1521*4882a593Smuzhiyun
1522*4882a593Smuzhiyun return 0;
1523*4882a593Smuzhiyun }
1524*4882a593Smuzhiyun
brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info * fws,u8 * data)1525*4882a593Smuzhiyun static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
1526*4882a593Smuzhiyun u8 *data)
1527*4882a593Smuzhiyun {
1528*4882a593Smuzhiyun int i;
1529*4882a593Smuzhiyun
1530*4882a593Smuzhiyun if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
1531*4882a593Smuzhiyun brcmf_dbg(INFO, "ignored\n");
1532*4882a593Smuzhiyun return BRCMF_FWS_RET_OK_NOSCHEDULE;
1533*4882a593Smuzhiyun }
1534*4882a593Smuzhiyun
1535*4882a593Smuzhiyun brcmf_dbg(DATA, "enter: data %pM\n", data);
1536*4882a593Smuzhiyun brcmf_fws_lock(fws);
1537*4882a593Smuzhiyun for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
1538*4882a593Smuzhiyun brcmf_fws_return_credits(fws, i, data[i]);
1539*4882a593Smuzhiyun
1540*4882a593Smuzhiyun brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
1541*4882a593Smuzhiyun fws->fifo_delay_map);
1542*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1543*4882a593Smuzhiyun return BRCMF_FWS_RET_OK_SCHEDULE;
1544*4882a593Smuzhiyun }
1545*4882a593Smuzhiyun
brcmf_fws_txstatus_indicate(struct brcmf_fws_info * fws,u8 type,u8 * data)1546*4882a593Smuzhiyun static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 type,
1547*4882a593Smuzhiyun u8 *data)
1548*4882a593Smuzhiyun {
1549*4882a593Smuzhiyun __le32 status_le;
1550*4882a593Smuzhiyun __le16 seq_le;
1551*4882a593Smuzhiyun u32 status;
1552*4882a593Smuzhiyun u32 hslot;
1553*4882a593Smuzhiyun u32 genbit;
1554*4882a593Smuzhiyun u8 flags;
1555*4882a593Smuzhiyun u16 seq;
1556*4882a593Smuzhiyun u8 compcnt;
1557*4882a593Smuzhiyun u8 compcnt_offset = BRCMF_FWS_TYPE_TXSTATUS_LEN;
1558*4882a593Smuzhiyun
1559*4882a593Smuzhiyun memcpy(&status_le, data, sizeof(status_le));
1560*4882a593Smuzhiyun status = le32_to_cpu(status_le);
1561*4882a593Smuzhiyun flags = brcmf_txstatus_get_field(status, FLAGS);
1562*4882a593Smuzhiyun hslot = brcmf_txstatus_get_field(status, HSLOT);
1563*4882a593Smuzhiyun genbit = brcmf_txstatus_get_field(status, GENERATION);
1564*4882a593Smuzhiyun if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
1565*4882a593Smuzhiyun memcpy(&seq_le, &data[BRCMF_FWS_TYPE_TXSTATUS_LEN],
1566*4882a593Smuzhiyun sizeof(seq_le));
1567*4882a593Smuzhiyun seq = le16_to_cpu(seq_le);
1568*4882a593Smuzhiyun compcnt_offset += BRCMF_FWS_TYPE_SEQ_LEN;
1569*4882a593Smuzhiyun } else {
1570*4882a593Smuzhiyun seq = 0;
1571*4882a593Smuzhiyun }
1572*4882a593Smuzhiyun
1573*4882a593Smuzhiyun if (type == BRCMF_FWS_TYPE_COMP_TXSTATUS)
1574*4882a593Smuzhiyun compcnt = data[compcnt_offset];
1575*4882a593Smuzhiyun else
1576*4882a593Smuzhiyun compcnt = 1;
1577*4882a593Smuzhiyun fws->stats.txs_indicate += compcnt;
1578*4882a593Smuzhiyun
1579*4882a593Smuzhiyun brcmf_fws_lock(fws);
1580*4882a593Smuzhiyun brcmf_fws_txs_process(fws, flags, hslot, genbit, seq, compcnt);
1581*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1582*4882a593Smuzhiyun return BRCMF_FWS_RET_OK_NOSCHEDULE;
1583*4882a593Smuzhiyun }
1584*4882a593Smuzhiyun
brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info * fws,u8 * data)1585*4882a593Smuzhiyun static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
1586*4882a593Smuzhiyun {
1587*4882a593Smuzhiyun __le32 timestamp;
1588*4882a593Smuzhiyun
1589*4882a593Smuzhiyun memcpy(×tamp, &data[2], sizeof(timestamp));
1590*4882a593Smuzhiyun brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],
1591*4882a593Smuzhiyun le32_to_cpu(timestamp));
1592*4882a593Smuzhiyun return 0;
1593*4882a593Smuzhiyun }
1594*4882a593Smuzhiyun
brcmf_fws_notify_credit_map(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)1595*4882a593Smuzhiyun static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
1596*4882a593Smuzhiyun const struct brcmf_event_msg *e,
1597*4882a593Smuzhiyun void *data)
1598*4882a593Smuzhiyun {
1599*4882a593Smuzhiyun struct brcmf_pub *drvr = ifp->drvr;
1600*4882a593Smuzhiyun struct brcmf_fws_info *fws = drvr_to_fws(drvr);
1601*4882a593Smuzhiyun int i;
1602*4882a593Smuzhiyun u8 *credits = data;
1603*4882a593Smuzhiyun
1604*4882a593Smuzhiyun if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
1605*4882a593Smuzhiyun bphy_err(drvr, "event payload too small (%d)\n", e->datalen);
1606*4882a593Smuzhiyun return -EINVAL;
1607*4882a593Smuzhiyun }
1608*4882a593Smuzhiyun
1609*4882a593Smuzhiyun fws->creditmap_received = true;
1610*4882a593Smuzhiyun
1611*4882a593Smuzhiyun brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
1612*4882a593Smuzhiyun brcmf_fws_lock(fws);
1613*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
1614*4882a593Smuzhiyun fws->fifo_credit[i] += credits[i] - fws->init_fifo_credit[i];
1615*4882a593Smuzhiyun fws->init_fifo_credit[i] = credits[i];
1616*4882a593Smuzhiyun if (fws->fifo_credit[i] > 0)
1617*4882a593Smuzhiyun fws->fifo_credit_map |= 1 << i;
1618*4882a593Smuzhiyun else
1619*4882a593Smuzhiyun fws->fifo_credit_map &= ~(1 << i);
1620*4882a593Smuzhiyun WARN_ONCE(fws->fifo_credit[i] < 0,
1621*4882a593Smuzhiyun "fifo_credit[%d] is negative(%d)\n", i,
1622*4882a593Smuzhiyun fws->fifo_credit[i]);
1623*4882a593Smuzhiyun }
1624*4882a593Smuzhiyun brcmf_fws_schedule_deq(fws);
1625*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1626*4882a593Smuzhiyun return 0;
1627*4882a593Smuzhiyun }
1628*4882a593Smuzhiyun
brcmf_fws_notify_bcmc_credit_support(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)1629*4882a593Smuzhiyun static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
1630*4882a593Smuzhiyun const struct brcmf_event_msg *e,
1631*4882a593Smuzhiyun void *data)
1632*4882a593Smuzhiyun {
1633*4882a593Smuzhiyun struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
1634*4882a593Smuzhiyun
1635*4882a593Smuzhiyun if (fws) {
1636*4882a593Smuzhiyun brcmf_fws_lock(fws);
1637*4882a593Smuzhiyun fws->bcmc_credit_check = true;
1638*4882a593Smuzhiyun brcmf_fws_unlock(fws);
1639*4882a593Smuzhiyun }
1640*4882a593Smuzhiyun return 0;
1641*4882a593Smuzhiyun }
1642*4882a593Smuzhiyun
brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder * rfi,u8 start,u8 end,struct sk_buff_head * skb_list)1643*4882a593Smuzhiyun static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
1644*4882a593Smuzhiyun u8 start, u8 end,
1645*4882a593Smuzhiyun struct sk_buff_head *skb_list)
1646*4882a593Smuzhiyun {
1647*4882a593Smuzhiyun /* initialize return list */
1648*4882a593Smuzhiyun __skb_queue_head_init(skb_list);
1649*4882a593Smuzhiyun
1650*4882a593Smuzhiyun if (rfi->pend_pkts == 0) {
1651*4882a593Smuzhiyun brcmf_dbg(INFO, "no packets in reorder queue\n");
1652*4882a593Smuzhiyun return;
1653*4882a593Smuzhiyun }
1654*4882a593Smuzhiyun
1655*4882a593Smuzhiyun do {
1656*4882a593Smuzhiyun if (rfi->pktslots[start]) {
1657*4882a593Smuzhiyun __skb_queue_tail(skb_list, rfi->pktslots[start]);
1658*4882a593Smuzhiyun rfi->pktslots[start] = NULL;
1659*4882a593Smuzhiyun }
1660*4882a593Smuzhiyun start++;
1661*4882a593Smuzhiyun if (start > rfi->max_idx)
1662*4882a593Smuzhiyun start = 0;
1663*4882a593Smuzhiyun } while (start != end);
1664*4882a593Smuzhiyun rfi->pend_pkts -= skb_queue_len(skb_list);
1665*4882a593Smuzhiyun }
1666*4882a593Smuzhiyun
brcmf_fws_rxreorder(struct brcmf_if * ifp,struct sk_buff * pkt,bool inirq)1667*4882a593Smuzhiyun void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt, bool inirq)
1668*4882a593Smuzhiyun {
1669*4882a593Smuzhiyun struct brcmf_pub *drvr = ifp->drvr;
1670*4882a593Smuzhiyun u8 *reorder_data;
1671*4882a593Smuzhiyun u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
1672*4882a593Smuzhiyun struct brcmf_ampdu_rx_reorder *rfi;
1673*4882a593Smuzhiyun struct sk_buff_head reorder_list;
1674*4882a593Smuzhiyun struct sk_buff *pnext;
1675*4882a593Smuzhiyun u8 flags;
1676*4882a593Smuzhiyun u32 buf_size;
1677*4882a593Smuzhiyun
1678*4882a593Smuzhiyun reorder_data = ((struct brcmf_skb_reorder_data *)pkt->cb)->reorder;
1679*4882a593Smuzhiyun flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
1680*4882a593Smuzhiyun flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
1681*4882a593Smuzhiyun
1682*4882a593Smuzhiyun /* validate flags and flow id */
1683*4882a593Smuzhiyun if (flags == 0xFF) {
1684*4882a593Smuzhiyun bphy_err(drvr, "invalid flags...so ignore this packet\n");
1685*4882a593Smuzhiyun brcmf_netif_rx(ifp, pkt, inirq);
1686*4882a593Smuzhiyun return;
1687*4882a593Smuzhiyun }
1688*4882a593Smuzhiyun
1689*4882a593Smuzhiyun rfi = ifp->drvr->reorder_flows[flow_id];
1690*4882a593Smuzhiyun if (flags & BRCMF_RXREORDER_DEL_FLOW) {
1691*4882a593Smuzhiyun brcmf_dbg(INFO, "flow-%d: delete\n",
1692*4882a593Smuzhiyun flow_id);
1693*4882a593Smuzhiyun
1694*4882a593Smuzhiyun if (rfi == NULL) {
1695*4882a593Smuzhiyun brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
1696*4882a593Smuzhiyun flow_id);
1697*4882a593Smuzhiyun brcmf_netif_rx(ifp, pkt, inirq);
1698*4882a593Smuzhiyun return;
1699*4882a593Smuzhiyun }
1700*4882a593Smuzhiyun
1701*4882a593Smuzhiyun brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
1702*4882a593Smuzhiyun &reorder_list);
1703*4882a593Smuzhiyun /* add the last packet */
1704*4882a593Smuzhiyun __skb_queue_tail(&reorder_list, pkt);
1705*4882a593Smuzhiyun kfree(rfi);
1706*4882a593Smuzhiyun ifp->drvr->reorder_flows[flow_id] = NULL;
1707*4882a593Smuzhiyun goto netif_rx;
1708*4882a593Smuzhiyun }
1709*4882a593Smuzhiyun /* from here on we need a flow reorder instance */
1710*4882a593Smuzhiyun if (rfi == NULL) {
1711*4882a593Smuzhiyun buf_size = sizeof(*rfi);
1712*4882a593Smuzhiyun max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
1713*4882a593Smuzhiyun
1714*4882a593Smuzhiyun buf_size += (max_idx + 1) * sizeof(pkt);
1715*4882a593Smuzhiyun
1716*4882a593Smuzhiyun /* allocate space for flow reorder info */
1717*4882a593Smuzhiyun brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
1718*4882a593Smuzhiyun flow_id, max_idx);
1719*4882a593Smuzhiyun rfi = kzalloc(buf_size, GFP_ATOMIC);
1720*4882a593Smuzhiyun if (rfi == NULL) {
1721*4882a593Smuzhiyun bphy_err(drvr, "failed to alloc buffer\n");
1722*4882a593Smuzhiyun brcmf_netif_rx(ifp, pkt, inirq);
1723*4882a593Smuzhiyun return;
1724*4882a593Smuzhiyun }
1725*4882a593Smuzhiyun
1726*4882a593Smuzhiyun ifp->drvr->reorder_flows[flow_id] = rfi;
1727*4882a593Smuzhiyun rfi->pktslots = (struct sk_buff **)(rfi + 1);
1728*4882a593Smuzhiyun rfi->max_idx = max_idx;
1729*4882a593Smuzhiyun }
1730*4882a593Smuzhiyun if (flags & BRCMF_RXREORDER_NEW_HOLE) {
1731*4882a593Smuzhiyun if (rfi->pend_pkts) {
1732*4882a593Smuzhiyun brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
1733*4882a593Smuzhiyun rfi->exp_idx,
1734*4882a593Smuzhiyun &reorder_list);
1735*4882a593Smuzhiyun WARN_ON(rfi->pend_pkts);
1736*4882a593Smuzhiyun } else {
1737*4882a593Smuzhiyun __skb_queue_head_init(&reorder_list);
1738*4882a593Smuzhiyun }
1739*4882a593Smuzhiyun rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
1740*4882a593Smuzhiyun rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
1741*4882a593Smuzhiyun rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
1742*4882a593Smuzhiyun rfi->pktslots[rfi->cur_idx] = pkt;
1743*4882a593Smuzhiyun rfi->pend_pkts++;
1744*4882a593Smuzhiyun brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
1745*4882a593Smuzhiyun flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
1746*4882a593Smuzhiyun } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
1747*4882a593Smuzhiyun cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
1748*4882a593Smuzhiyun exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
1749*4882a593Smuzhiyun
1750*4882a593Smuzhiyun if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
1751*4882a593Smuzhiyun /* still in the current hole */
1752*4882a593Smuzhiyun /* enqueue the current on the buffer chain */
1753*4882a593Smuzhiyun if (rfi->pktslots[cur_idx] != NULL) {
1754*4882a593Smuzhiyun brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
1755*4882a593Smuzhiyun brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
1756*4882a593Smuzhiyun rfi->pktslots[cur_idx] = NULL;
1757*4882a593Smuzhiyun }
1758*4882a593Smuzhiyun rfi->pktslots[cur_idx] = pkt;
1759*4882a593Smuzhiyun rfi->pend_pkts++;
1760*4882a593Smuzhiyun rfi->cur_idx = cur_idx;
1761*4882a593Smuzhiyun brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
1762*4882a593Smuzhiyun flow_id, cur_idx, exp_idx, rfi->pend_pkts);
1763*4882a593Smuzhiyun
1764*4882a593Smuzhiyun /* can return now as there is no reorder
1765*4882a593Smuzhiyun * list to process.
1766*4882a593Smuzhiyun */
1767*4882a593Smuzhiyun return;
1768*4882a593Smuzhiyun }
1769*4882a593Smuzhiyun if (rfi->exp_idx == cur_idx) {
1770*4882a593Smuzhiyun if (rfi->pktslots[cur_idx] != NULL) {
1771*4882a593Smuzhiyun brcmf_dbg(INFO, "error buffer pending..free it\n");
1772*4882a593Smuzhiyun brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
1773*4882a593Smuzhiyun rfi->pktslots[cur_idx] = NULL;
1774*4882a593Smuzhiyun }
1775*4882a593Smuzhiyun rfi->pktslots[cur_idx] = pkt;
1776*4882a593Smuzhiyun rfi->pend_pkts++;
1777*4882a593Smuzhiyun
1778*4882a593Smuzhiyun /* got the expected one. flush from current to expected
1779*4882a593Smuzhiyun * and update expected
1780*4882a593Smuzhiyun */
1781*4882a593Smuzhiyun brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
1782*4882a593Smuzhiyun flow_id, cur_idx, exp_idx, rfi->pend_pkts);
1783*4882a593Smuzhiyun
1784*4882a593Smuzhiyun rfi->cur_idx = cur_idx;
1785*4882a593Smuzhiyun rfi->exp_idx = exp_idx;
1786*4882a593Smuzhiyun
1787*4882a593Smuzhiyun brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
1788*4882a593Smuzhiyun &reorder_list);
1789*4882a593Smuzhiyun brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
1790*4882a593Smuzhiyun flow_id, skb_queue_len(&reorder_list),
1791*4882a593Smuzhiyun rfi->pend_pkts);
1792*4882a593Smuzhiyun } else {
1793*4882a593Smuzhiyun u8 end_idx;
1794*4882a593Smuzhiyun
1795*4882a593Smuzhiyun brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
1796*4882a593Smuzhiyun flow_id, flags, rfi->cur_idx, rfi->exp_idx,
1797*4882a593Smuzhiyun cur_idx, exp_idx);
1798*4882a593Smuzhiyun if (flags & BRCMF_RXREORDER_FLUSH_ALL)
1799*4882a593Smuzhiyun end_idx = rfi->exp_idx;
1800*4882a593Smuzhiyun else
1801*4882a593Smuzhiyun end_idx = exp_idx;
1802*4882a593Smuzhiyun
1803*4882a593Smuzhiyun /* flush pkts first */
1804*4882a593Smuzhiyun brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
1805*4882a593Smuzhiyun &reorder_list);
1806*4882a593Smuzhiyun
1807*4882a593Smuzhiyun if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
1808*4882a593Smuzhiyun __skb_queue_tail(&reorder_list, pkt);
1809*4882a593Smuzhiyun } else {
1810*4882a593Smuzhiyun rfi->pktslots[cur_idx] = pkt;
1811*4882a593Smuzhiyun rfi->pend_pkts++;
1812*4882a593Smuzhiyun }
1813*4882a593Smuzhiyun rfi->exp_idx = exp_idx;
1814*4882a593Smuzhiyun rfi->cur_idx = cur_idx;
1815*4882a593Smuzhiyun }
1816*4882a593Smuzhiyun } else {
1817*4882a593Smuzhiyun /* explicity window move updating the expected index */
1818*4882a593Smuzhiyun exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
1819*4882a593Smuzhiyun
1820*4882a593Smuzhiyun brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
1821*4882a593Smuzhiyun flow_id, flags, rfi->exp_idx, exp_idx);
1822*4882a593Smuzhiyun if (flags & BRCMF_RXREORDER_FLUSH_ALL)
1823*4882a593Smuzhiyun end_idx = rfi->exp_idx;
1824*4882a593Smuzhiyun else
1825*4882a593Smuzhiyun end_idx = exp_idx;
1826*4882a593Smuzhiyun
1827*4882a593Smuzhiyun brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
1828*4882a593Smuzhiyun &reorder_list);
1829*4882a593Smuzhiyun __skb_queue_tail(&reorder_list, pkt);
1830*4882a593Smuzhiyun /* set the new expected idx */
1831*4882a593Smuzhiyun rfi->exp_idx = exp_idx;
1832*4882a593Smuzhiyun }
1833*4882a593Smuzhiyun netif_rx:
1834*4882a593Smuzhiyun skb_queue_walk_safe(&reorder_list, pkt, pnext) {
1835*4882a593Smuzhiyun __skb_unlink(pkt, &reorder_list);
1836*4882a593Smuzhiyun brcmf_netif_rx(ifp, pkt, inirq);
1837*4882a593Smuzhiyun }
1838*4882a593Smuzhiyun }
1839*4882a593Smuzhiyun
brcmf_fws_hdrpull(struct brcmf_if * ifp,s16 siglen,struct sk_buff * skb)1840*4882a593Smuzhiyun void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
1841*4882a593Smuzhiyun {
1842*4882a593Smuzhiyun struct brcmf_skb_reorder_data *rd;
1843*4882a593Smuzhiyun struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
1844*4882a593Smuzhiyun u8 *signal_data;
1845*4882a593Smuzhiyun s16 data_len;
1846*4882a593Smuzhiyun u8 type;
1847*4882a593Smuzhiyun u8 len;
1848*4882a593Smuzhiyun u8 *data;
1849*4882a593Smuzhiyun s32 status;
1850*4882a593Smuzhiyun s32 err;
1851*4882a593Smuzhiyun
1852*4882a593Smuzhiyun brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
1853*4882a593Smuzhiyun ifp->ifidx, skb->len, siglen);
1854*4882a593Smuzhiyun
1855*4882a593Smuzhiyun WARN_ON(siglen > skb->len);
1856*4882a593Smuzhiyun
1857*4882a593Smuzhiyun if (siglen > skb->len)
1858*4882a593Smuzhiyun siglen = skb->len;
1859*4882a593Smuzhiyun
1860*4882a593Smuzhiyun if (!siglen)
1861*4882a593Smuzhiyun return;
1862*4882a593Smuzhiyun /* if flow control disabled, skip to packet data and leave */
1863*4882a593Smuzhiyun if ((!fws) || (!fws->fw_signals)) {
1864*4882a593Smuzhiyun skb_pull(skb, siglen);
1865*4882a593Smuzhiyun return;
1866*4882a593Smuzhiyun }
1867*4882a593Smuzhiyun
1868*4882a593Smuzhiyun fws->stats.header_pulls++;
1869*4882a593Smuzhiyun data_len = siglen;
1870*4882a593Smuzhiyun signal_data = skb->data;
1871*4882a593Smuzhiyun
1872*4882a593Smuzhiyun status = BRCMF_FWS_RET_OK_NOSCHEDULE;
1873*4882a593Smuzhiyun while (data_len > 0) {
1874*4882a593Smuzhiyun /* extract tlv info */
1875*4882a593Smuzhiyun type = signal_data[0];
1876*4882a593Smuzhiyun
1877*4882a593Smuzhiyun /* FILLER type is actually not a TLV, but
1878*4882a593Smuzhiyun * a single byte that can be skipped.
1879*4882a593Smuzhiyun */
1880*4882a593Smuzhiyun if (type == BRCMF_FWS_TYPE_FILLER) {
1881*4882a593Smuzhiyun signal_data += 1;
1882*4882a593Smuzhiyun data_len -= 1;
1883*4882a593Smuzhiyun continue;
1884*4882a593Smuzhiyun }
1885*4882a593Smuzhiyun len = signal_data[1];
1886*4882a593Smuzhiyun data = signal_data + 2;
1887*4882a593Smuzhiyun
1888*4882a593Smuzhiyun brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n",
1889*4882a593Smuzhiyun brcmf_fws_get_tlv_name(type), type, len,
1890*4882a593Smuzhiyun brcmf_fws_get_tlv_len(fws, type));
1891*4882a593Smuzhiyun
1892*4882a593Smuzhiyun /* abort parsing when length invalid */
1893*4882a593Smuzhiyun if (data_len < len + 2)
1894*4882a593Smuzhiyun break;
1895*4882a593Smuzhiyun
1896*4882a593Smuzhiyun if (len < brcmf_fws_get_tlv_len(fws, type))
1897*4882a593Smuzhiyun break;
1898*4882a593Smuzhiyun
1899*4882a593Smuzhiyun err = BRCMF_FWS_RET_OK_NOSCHEDULE;
1900*4882a593Smuzhiyun switch (type) {
1901*4882a593Smuzhiyun case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
1902*4882a593Smuzhiyun rd = (struct brcmf_skb_reorder_data *)skb->cb;
1903*4882a593Smuzhiyun rd->reorder = data;
1904*4882a593Smuzhiyun break;
1905*4882a593Smuzhiyun case BRCMF_FWS_TYPE_MACDESC_ADD:
1906*4882a593Smuzhiyun case BRCMF_FWS_TYPE_MACDESC_DEL:
1907*4882a593Smuzhiyun brcmf_fws_macdesc_indicate(fws, type, data);
1908*4882a593Smuzhiyun break;
1909*4882a593Smuzhiyun case BRCMF_FWS_TYPE_MAC_OPEN:
1910*4882a593Smuzhiyun case BRCMF_FWS_TYPE_MAC_CLOSE:
1911*4882a593Smuzhiyun err = brcmf_fws_macdesc_state_indicate(fws, type, data);
1912*4882a593Smuzhiyun break;
1913*4882a593Smuzhiyun case BRCMF_FWS_TYPE_INTERFACE_OPEN:
1914*4882a593Smuzhiyun case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
1915*4882a593Smuzhiyun err = brcmf_fws_interface_state_indicate(fws, type,
1916*4882a593Smuzhiyun data);
1917*4882a593Smuzhiyun break;
1918*4882a593Smuzhiyun case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
1919*4882a593Smuzhiyun case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
1920*4882a593Smuzhiyun err = brcmf_fws_request_indicate(fws, type, data);
1921*4882a593Smuzhiyun break;
1922*4882a593Smuzhiyun case BRCMF_FWS_TYPE_TXSTATUS:
1923*4882a593Smuzhiyun case BRCMF_FWS_TYPE_COMP_TXSTATUS:
1924*4882a593Smuzhiyun brcmf_fws_txstatus_indicate(fws, type, data);
1925*4882a593Smuzhiyun break;
1926*4882a593Smuzhiyun case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
1927*4882a593Smuzhiyun err = brcmf_fws_fifocreditback_indicate(fws, data);
1928*4882a593Smuzhiyun break;
1929*4882a593Smuzhiyun case BRCMF_FWS_TYPE_RSSI:
1930*4882a593Smuzhiyun brcmf_fws_rssi_indicate(fws, *data);
1931*4882a593Smuzhiyun break;
1932*4882a593Smuzhiyun case BRCMF_FWS_TYPE_TRANS_ID:
1933*4882a593Smuzhiyun brcmf_fws_dbg_seqnum_check(fws, data);
1934*4882a593Smuzhiyun break;
1935*4882a593Smuzhiyun case BRCMF_FWS_TYPE_PKTTAG:
1936*4882a593Smuzhiyun case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP:
1937*4882a593Smuzhiyun default:
1938*4882a593Smuzhiyun fws->stats.tlv_invalid_type++;
1939*4882a593Smuzhiyun break;
1940*4882a593Smuzhiyun }
1941*4882a593Smuzhiyun if (err == BRCMF_FWS_RET_OK_SCHEDULE)
1942*4882a593Smuzhiyun status = BRCMF_FWS_RET_OK_SCHEDULE;
1943*4882a593Smuzhiyun signal_data += len + 2;
1944*4882a593Smuzhiyun data_len -= len + 2;
1945*4882a593Smuzhiyun }
1946*4882a593Smuzhiyun
1947*4882a593Smuzhiyun if (data_len != 0)
1948*4882a593Smuzhiyun fws->stats.tlv_parse_failed++;
1949*4882a593Smuzhiyun
1950*4882a593Smuzhiyun if (status == BRCMF_FWS_RET_OK_SCHEDULE)
1951*4882a593Smuzhiyun brcmf_fws_schedule_deq(fws);
1952*4882a593Smuzhiyun
1953*4882a593Smuzhiyun /* signalling processing result does
1954*4882a593Smuzhiyun * not affect the actual ethernet packet.
1955*4882a593Smuzhiyun */
1956*4882a593Smuzhiyun skb_pull(skb, siglen);
1957*4882a593Smuzhiyun
1958*4882a593Smuzhiyun /* this may be a signal-only packet
1959*4882a593Smuzhiyun */
1960*4882a593Smuzhiyun if (skb->len == 0)
1961*4882a593Smuzhiyun fws->stats.header_only_pkt++;
1962*4882a593Smuzhiyun }
1963*4882a593Smuzhiyun
brcmf_fws_precommit_skb(struct brcmf_fws_info * fws,int fifo,struct sk_buff * p)1964*4882a593Smuzhiyun static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
1965*4882a593Smuzhiyun struct sk_buff *p)
1966*4882a593Smuzhiyun {
1967*4882a593Smuzhiyun struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
1968*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry = skcb->mac;
1969*4882a593Smuzhiyun u8 flags;
1970*4882a593Smuzhiyun
1971*4882a593Smuzhiyun if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED)
1972*4882a593Smuzhiyun brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);
1973*4882a593Smuzhiyun flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
1974*4882a593Smuzhiyun if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
1975*4882a593Smuzhiyun /*
1976*4882a593Smuzhiyun * Indicate that this packet is being sent in response to an
1977*4882a593Smuzhiyun * explicit request from the firmware side.
1978*4882a593Smuzhiyun */
1979*4882a593Smuzhiyun flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
1980*4882a593Smuzhiyun }
1981*4882a593Smuzhiyun brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
1982*4882a593Smuzhiyun return brcmf_fws_hdrpush(fws, p);
1983*4882a593Smuzhiyun }
1984*4882a593Smuzhiyun
brcmf_fws_rollback_toq(struct brcmf_fws_info * fws,struct sk_buff * skb,int fifo)1985*4882a593Smuzhiyun static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
1986*4882a593Smuzhiyun struct sk_buff *skb, int fifo)
1987*4882a593Smuzhiyun {
1988*4882a593Smuzhiyun struct brcmf_pub *drvr = fws->drvr;
1989*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry;
1990*4882a593Smuzhiyun struct sk_buff *pktout;
1991*4882a593Smuzhiyun int qidx, hslot;
1992*4882a593Smuzhiyun int rc = 0;
1993*4882a593Smuzhiyun
1994*4882a593Smuzhiyun entry = brcmf_skbcb(skb)->mac;
1995*4882a593Smuzhiyun if (entry->occupied) {
1996*4882a593Smuzhiyun qidx = 2 * fifo;
1997*4882a593Smuzhiyun if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
1998*4882a593Smuzhiyun qidx++;
1999*4882a593Smuzhiyun
2000*4882a593Smuzhiyun pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb);
2001*4882a593Smuzhiyun if (pktout == NULL) {
2002*4882a593Smuzhiyun bphy_err(drvr, "%s queue %d full\n", entry->name, qidx);
2003*4882a593Smuzhiyun rc = -ENOSPC;
2004*4882a593Smuzhiyun }
2005*4882a593Smuzhiyun } else {
2006*4882a593Smuzhiyun bphy_err(drvr, "%s entry removed\n", entry->name);
2007*4882a593Smuzhiyun rc = -ENOENT;
2008*4882a593Smuzhiyun }
2009*4882a593Smuzhiyun
2010*4882a593Smuzhiyun if (rc) {
2011*4882a593Smuzhiyun fws->stats.rollback_failed++;
2012*4882a593Smuzhiyun hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
2013*4882a593Smuzhiyun brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
2014*4882a593Smuzhiyun hslot, 0, 0, 1);
2015*4882a593Smuzhiyun } else {
2016*4882a593Smuzhiyun fws->stats.rollback_success++;
2017*4882a593Smuzhiyun brcmf_fws_return_credits(fws, fifo, 1);
2018*4882a593Smuzhiyun brcmf_fws_macdesc_return_req_credit(skb);
2019*4882a593Smuzhiyun }
2020*4882a593Smuzhiyun }
2021*4882a593Smuzhiyun
brcmf_fws_borrow_credit(struct brcmf_fws_info * fws,int highest_lender_ac,int borrower_ac,bool borrow_all)2022*4882a593Smuzhiyun static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws,
2023*4882a593Smuzhiyun int highest_lender_ac, int borrower_ac,
2024*4882a593Smuzhiyun bool borrow_all)
2025*4882a593Smuzhiyun {
2026*4882a593Smuzhiyun int lender_ac, borrow_limit = 0;
2027*4882a593Smuzhiyun
2028*4882a593Smuzhiyun for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {
2029*4882a593Smuzhiyun
2030*4882a593Smuzhiyun if (!borrow_all)
2031*4882a593Smuzhiyun borrow_limit =
2032*4882a593Smuzhiyun fws->init_fifo_credit[lender_ac] / BRCMF_BORROW_RATIO;
2033*4882a593Smuzhiyun else
2034*4882a593Smuzhiyun borrow_limit = 0;
2035*4882a593Smuzhiyun
2036*4882a593Smuzhiyun if (fws->fifo_credit[lender_ac] > borrow_limit) {
2037*4882a593Smuzhiyun fws->credits_borrowed[borrower_ac][lender_ac]++;
2038*4882a593Smuzhiyun fws->fifo_credit[lender_ac]--;
2039*4882a593Smuzhiyun if (fws->fifo_credit[lender_ac] == 0)
2040*4882a593Smuzhiyun fws->fifo_credit_map &= ~(1 << lender_ac);
2041*4882a593Smuzhiyun fws->fifo_credit_map |= (1 << borrower_ac);
2042*4882a593Smuzhiyun brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac);
2043*4882a593Smuzhiyun return 0;
2044*4882a593Smuzhiyun }
2045*4882a593Smuzhiyun }
2046*4882a593Smuzhiyun fws->fifo_credit_map &= ~(1 << borrower_ac);
2047*4882a593Smuzhiyun return -ENAVAIL;
2048*4882a593Smuzhiyun }
2049*4882a593Smuzhiyun
brcmf_fws_commit_skb(struct brcmf_fws_info * fws,int fifo,struct sk_buff * skb)2050*4882a593Smuzhiyun static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
2051*4882a593Smuzhiyun struct sk_buff *skb)
2052*4882a593Smuzhiyun {
2053*4882a593Smuzhiyun struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
2054*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry;
2055*4882a593Smuzhiyun int rc;
2056*4882a593Smuzhiyun u8 ifidx;
2057*4882a593Smuzhiyun u8 data_offset;
2058*4882a593Smuzhiyun
2059*4882a593Smuzhiyun entry = skcb->mac;
2060*4882a593Smuzhiyun if (IS_ERR(entry))
2061*4882a593Smuzhiyun return PTR_ERR(entry);
2062*4882a593Smuzhiyun
2063*4882a593Smuzhiyun data_offset = brcmf_fws_precommit_skb(fws, fifo, skb);
2064*4882a593Smuzhiyun entry->transit_count++;
2065*4882a593Smuzhiyun if (entry->suppressed)
2066*4882a593Smuzhiyun entry->suppr_transit_count++;
2067*4882a593Smuzhiyun ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
2068*4882a593Smuzhiyun brcmf_fws_unlock(fws);
2069*4882a593Smuzhiyun rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
2070*4882a593Smuzhiyun brcmf_fws_lock(fws);
2071*4882a593Smuzhiyun brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
2072*4882a593Smuzhiyun skcb->if_flags, skcb->htod, rc);
2073*4882a593Smuzhiyun if (rc < 0) {
2074*4882a593Smuzhiyun entry->transit_count--;
2075*4882a593Smuzhiyun if (entry->suppressed)
2076*4882a593Smuzhiyun entry->suppr_transit_count--;
2077*4882a593Smuzhiyun (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
2078*4882a593Smuzhiyun goto rollback;
2079*4882a593Smuzhiyun }
2080*4882a593Smuzhiyun
2081*4882a593Smuzhiyun fws->stats.pkt2bus++;
2082*4882a593Smuzhiyun fws->stats.send_pkts[fifo]++;
2083*4882a593Smuzhiyun if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
2084*4882a593Smuzhiyun fws->stats.requested_sent[fifo]++;
2085*4882a593Smuzhiyun
2086*4882a593Smuzhiyun return rc;
2087*4882a593Smuzhiyun
2088*4882a593Smuzhiyun rollback:
2089*4882a593Smuzhiyun brcmf_fws_rollback_toq(fws, skb, fifo);
2090*4882a593Smuzhiyun return rc;
2091*4882a593Smuzhiyun }
2092*4882a593Smuzhiyun
brcmf_fws_assign_htod(struct brcmf_fws_info * fws,struct sk_buff * p,int fifo)2093*4882a593Smuzhiyun static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,
2094*4882a593Smuzhiyun int fifo)
2095*4882a593Smuzhiyun {
2096*4882a593Smuzhiyun struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
2097*4882a593Smuzhiyun int rc, hslot;
2098*4882a593Smuzhiyun
2099*4882a593Smuzhiyun skcb->htod = 0;
2100*4882a593Smuzhiyun skcb->htod_seq = 0;
2101*4882a593Smuzhiyun hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
2102*4882a593Smuzhiyun brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
2103*4882a593Smuzhiyun brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);
2104*4882a593Smuzhiyun brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
2105*4882a593Smuzhiyun rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
2106*4882a593Smuzhiyun if (!rc)
2107*4882a593Smuzhiyun skcb->mac->seq[fifo]++;
2108*4882a593Smuzhiyun else
2109*4882a593Smuzhiyun fws->stats.generic_error++;
2110*4882a593Smuzhiyun return rc;
2111*4882a593Smuzhiyun }
2112*4882a593Smuzhiyun
brcmf_fws_process_skb(struct brcmf_if * ifp,struct sk_buff * skb)2113*4882a593Smuzhiyun int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
2114*4882a593Smuzhiyun {
2115*4882a593Smuzhiyun struct brcmf_pub *drvr = ifp->drvr;
2116*4882a593Smuzhiyun struct brcmf_fws_info *fws = drvr_to_fws(drvr);
2117*4882a593Smuzhiyun struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
2118*4882a593Smuzhiyun struct ethhdr *eh = (struct ethhdr *)(skb->data);
2119*4882a593Smuzhiyun int fifo = BRCMF_FWS_FIFO_BCMC;
2120*4882a593Smuzhiyun bool multicast = is_multicast_ether_addr(eh->h_dest);
2121*4882a593Smuzhiyun int rc = 0;
2122*4882a593Smuzhiyun
2123*4882a593Smuzhiyun brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
2124*4882a593Smuzhiyun
2125*4882a593Smuzhiyun /* set control buffer information */
2126*4882a593Smuzhiyun skcb->if_flags = 0;
2127*4882a593Smuzhiyun skcb->state = BRCMF_FWS_SKBSTATE_NEW;
2128*4882a593Smuzhiyun brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
2129*4882a593Smuzhiyun
2130*4882a593Smuzhiyun /* mapping from 802.1d priority to firmware fifo index */
2131*4882a593Smuzhiyun if (!multicast)
2132*4882a593Smuzhiyun fifo = brcmf_map_prio_to_aci(drvr->config, skb->priority);
2133*4882a593Smuzhiyun
2134*4882a593Smuzhiyun brcmf_fws_lock(fws);
2135*4882a593Smuzhiyun if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
2136*4882a593Smuzhiyun fws->borrow_defer_timestamp = jiffies +
2137*4882a593Smuzhiyun BRCMF_FWS_BORROW_DEFER_PERIOD;
2138*4882a593Smuzhiyun
2139*4882a593Smuzhiyun skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);
2140*4882a593Smuzhiyun brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,
2141*4882a593Smuzhiyun eh->h_dest, multicast, fifo);
2142*4882a593Smuzhiyun if (!brcmf_fws_assign_htod(fws, skb, fifo)) {
2143*4882a593Smuzhiyun brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
2144*4882a593Smuzhiyun brcmf_fws_schedule_deq(fws);
2145*4882a593Smuzhiyun } else {
2146*4882a593Smuzhiyun bphy_err(drvr, "no hanger slot available\n");
2147*4882a593Smuzhiyun rc = -ENOMEM;
2148*4882a593Smuzhiyun }
2149*4882a593Smuzhiyun brcmf_fws_unlock(fws);
2150*4882a593Smuzhiyun
2151*4882a593Smuzhiyun return rc;
2152*4882a593Smuzhiyun }
2153*4882a593Smuzhiyun
brcmf_fws_reset_interface(struct brcmf_if * ifp)2154*4882a593Smuzhiyun void brcmf_fws_reset_interface(struct brcmf_if *ifp)
2155*4882a593Smuzhiyun {
2156*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
2157*4882a593Smuzhiyun
2158*4882a593Smuzhiyun brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
2159*4882a593Smuzhiyun if (!entry)
2160*4882a593Smuzhiyun return;
2161*4882a593Smuzhiyun
2162*4882a593Smuzhiyun brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
2163*4882a593Smuzhiyun }
2164*4882a593Smuzhiyun
brcmf_fws_add_interface(struct brcmf_if * ifp)2165*4882a593Smuzhiyun void brcmf_fws_add_interface(struct brcmf_if *ifp)
2166*4882a593Smuzhiyun {
2167*4882a593Smuzhiyun struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
2168*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry;
2169*4882a593Smuzhiyun
2170*4882a593Smuzhiyun if (!ifp->ndev || !brcmf_fws_queue_skbs(fws))
2171*4882a593Smuzhiyun return;
2172*4882a593Smuzhiyun
2173*4882a593Smuzhiyun entry = &fws->desc.iface[ifp->ifidx];
2174*4882a593Smuzhiyun ifp->fws_desc = entry;
2175*4882a593Smuzhiyun brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
2176*4882a593Smuzhiyun brcmf_fws_macdesc_set_name(fws, entry);
2177*4882a593Smuzhiyun brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
2178*4882a593Smuzhiyun BRCMF_FWS_PSQ_LEN);
2179*4882a593Smuzhiyun brcmf_dbg(TRACE, "added %s\n", entry->name);
2180*4882a593Smuzhiyun }
2181*4882a593Smuzhiyun
brcmf_fws_del_interface(struct brcmf_if * ifp)2182*4882a593Smuzhiyun void brcmf_fws_del_interface(struct brcmf_if *ifp)
2183*4882a593Smuzhiyun {
2184*4882a593Smuzhiyun struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
2185*4882a593Smuzhiyun struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
2186*4882a593Smuzhiyun
2187*4882a593Smuzhiyun if (!entry)
2188*4882a593Smuzhiyun return;
2189*4882a593Smuzhiyun
2190*4882a593Smuzhiyun brcmf_fws_lock(fws);
2191*4882a593Smuzhiyun ifp->fws_desc = NULL;
2192*4882a593Smuzhiyun brcmf_dbg(TRACE, "deleting %s\n", entry->name);
2193*4882a593Smuzhiyun brcmf_fws_macdesc_cleanup(fws, &fws->desc.iface[ifp->ifidx],
2194*4882a593Smuzhiyun ifp->ifidx);
2195*4882a593Smuzhiyun brcmf_fws_macdesc_deinit(entry);
2196*4882a593Smuzhiyun brcmf_fws_cleanup(fws, ifp->ifidx);
2197*4882a593Smuzhiyun brcmf_fws_unlock(fws);
2198*4882a593Smuzhiyun }
2199*4882a593Smuzhiyun
brcmf_fws_dequeue_worker(struct work_struct * worker)2200*4882a593Smuzhiyun static void brcmf_fws_dequeue_worker(struct work_struct *worker)
2201*4882a593Smuzhiyun {
2202*4882a593Smuzhiyun struct brcmf_fws_info *fws;
2203*4882a593Smuzhiyun struct brcmf_pub *drvr;
2204*4882a593Smuzhiyun struct sk_buff *skb;
2205*4882a593Smuzhiyun int fifo;
2206*4882a593Smuzhiyun u32 hslot;
2207*4882a593Smuzhiyun u32 ifidx;
2208*4882a593Smuzhiyun int ret;
2209*4882a593Smuzhiyun
2210*4882a593Smuzhiyun fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
2211*4882a593Smuzhiyun drvr = fws->drvr;
2212*4882a593Smuzhiyun
2213*4882a593Smuzhiyun brcmf_fws_lock(fws);
2214*4882a593Smuzhiyun for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
2215*4882a593Smuzhiyun fifo--) {
2216*4882a593Smuzhiyun if (!brcmf_fws_fc_active(fws)) {
2217*4882a593Smuzhiyun while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
2218*4882a593Smuzhiyun hslot = brcmf_skb_htod_tag_get_field(skb,
2219*4882a593Smuzhiyun HSLOT);
2220*4882a593Smuzhiyun brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
2221*4882a593Smuzhiyun &skb, true);
2222*4882a593Smuzhiyun ifidx = brcmf_skb_if_flags_get_field(skb,
2223*4882a593Smuzhiyun INDEX);
2224*4882a593Smuzhiyun /* Use proto layer to send data frame */
2225*4882a593Smuzhiyun brcmf_fws_unlock(fws);
2226*4882a593Smuzhiyun ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
2227*4882a593Smuzhiyun brcmf_fws_lock(fws);
2228*4882a593Smuzhiyun if (ret < 0)
2229*4882a593Smuzhiyun brcmf_txfinalize(brcmf_get_ifp(drvr,
2230*4882a593Smuzhiyun ifidx),
2231*4882a593Smuzhiyun skb, false);
2232*4882a593Smuzhiyun if (fws->bus_flow_blocked)
2233*4882a593Smuzhiyun break;
2234*4882a593Smuzhiyun }
2235*4882a593Smuzhiyun continue;
2236*4882a593Smuzhiyun }
2237*4882a593Smuzhiyun
2238*4882a593Smuzhiyun while ((fws->fifo_credit[fifo]) ||
2239*4882a593Smuzhiyun ((!fws->bcmc_credit_check) &&
2240*4882a593Smuzhiyun (fifo == BRCMF_FWS_FIFO_BCMC))) {
2241*4882a593Smuzhiyun skb = brcmf_fws_deq(fws, fifo);
2242*4882a593Smuzhiyun if (!skb)
2243*4882a593Smuzhiyun break;
2244*4882a593Smuzhiyun fws->fifo_credit[fifo]--;
2245*4882a593Smuzhiyun if (brcmf_fws_commit_skb(fws, fifo, skb))
2246*4882a593Smuzhiyun break;
2247*4882a593Smuzhiyun if (fws->bus_flow_blocked)
2248*4882a593Smuzhiyun break;
2249*4882a593Smuzhiyun }
2250*4882a593Smuzhiyun
2251*4882a593Smuzhiyun if (fifo >= BRCMF_FWS_FIFO_AC_BE &&
2252*4882a593Smuzhiyun fifo <= BRCMF_FWS_FIFO_AC_VO &&
2253*4882a593Smuzhiyun fws->fifo_credit[fifo] == 0 &&
2254*4882a593Smuzhiyun !fws->bus_flow_blocked) {
2255*4882a593Smuzhiyun while (brcmf_fws_borrow_credit(fws,
2256*4882a593Smuzhiyun fifo - 1, fifo,
2257*4882a593Smuzhiyun true) == 0) {
2258*4882a593Smuzhiyun skb = brcmf_fws_deq(fws, fifo);
2259*4882a593Smuzhiyun if (!skb) {
2260*4882a593Smuzhiyun brcmf_fws_return_credits(fws, fifo, 1);
2261*4882a593Smuzhiyun break;
2262*4882a593Smuzhiyun }
2263*4882a593Smuzhiyun if (brcmf_fws_commit_skb(fws, fifo, skb))
2264*4882a593Smuzhiyun break;
2265*4882a593Smuzhiyun if (fws->bus_flow_blocked)
2266*4882a593Smuzhiyun break;
2267*4882a593Smuzhiyun }
2268*4882a593Smuzhiyun }
2269*4882a593Smuzhiyun }
2270*4882a593Smuzhiyun brcmf_fws_unlock(fws);
2271*4882a593Smuzhiyun }
2272*4882a593Smuzhiyun
2273*4882a593Smuzhiyun #ifdef DEBUG
brcmf_debugfs_fws_stats_read(struct seq_file * seq,void * data)2274*4882a593Smuzhiyun static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
2275*4882a593Smuzhiyun {
2276*4882a593Smuzhiyun struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
2277*4882a593Smuzhiyun struct brcmf_fws_stats *fwstats = &(drvr_to_fws(bus_if->drvr)->stats);
2278*4882a593Smuzhiyun
2279*4882a593Smuzhiyun seq_printf(seq,
2280*4882a593Smuzhiyun "header_pulls: %u\n"
2281*4882a593Smuzhiyun "header_only_pkt: %u\n"
2282*4882a593Smuzhiyun "tlv_parse_failed: %u\n"
2283*4882a593Smuzhiyun "tlv_invalid_type: %u\n"
2284*4882a593Smuzhiyun "mac_update_fails: %u\n"
2285*4882a593Smuzhiyun "ps_update_fails: %u\n"
2286*4882a593Smuzhiyun "if_update_fails: %u\n"
2287*4882a593Smuzhiyun "pkt2bus: %u\n"
2288*4882a593Smuzhiyun "generic_error: %u\n"
2289*4882a593Smuzhiyun "rollback_success: %u\n"
2290*4882a593Smuzhiyun "rollback_failed: %u\n"
2291*4882a593Smuzhiyun "delayq_full: %u\n"
2292*4882a593Smuzhiyun "supprq_full: %u\n"
2293*4882a593Smuzhiyun "txs_indicate: %u\n"
2294*4882a593Smuzhiyun "txs_discard: %u\n"
2295*4882a593Smuzhiyun "txs_suppr_core: %u\n"
2296*4882a593Smuzhiyun "txs_suppr_ps: %u\n"
2297*4882a593Smuzhiyun "txs_tossed: %u\n"
2298*4882a593Smuzhiyun "txs_host_tossed: %u\n"
2299*4882a593Smuzhiyun "bus_flow_block: %u\n"
2300*4882a593Smuzhiyun "fws_flow_block: %u\n"
2301*4882a593Smuzhiyun "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
2302*4882a593Smuzhiyun "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
2303*4882a593Smuzhiyun fwstats->header_pulls,
2304*4882a593Smuzhiyun fwstats->header_only_pkt,
2305*4882a593Smuzhiyun fwstats->tlv_parse_failed,
2306*4882a593Smuzhiyun fwstats->tlv_invalid_type,
2307*4882a593Smuzhiyun fwstats->mac_update_failed,
2308*4882a593Smuzhiyun fwstats->mac_ps_update_failed,
2309*4882a593Smuzhiyun fwstats->if_update_failed,
2310*4882a593Smuzhiyun fwstats->pkt2bus,
2311*4882a593Smuzhiyun fwstats->generic_error,
2312*4882a593Smuzhiyun fwstats->rollback_success,
2313*4882a593Smuzhiyun fwstats->rollback_failed,
2314*4882a593Smuzhiyun fwstats->delayq_full_error,
2315*4882a593Smuzhiyun fwstats->supprq_full_error,
2316*4882a593Smuzhiyun fwstats->txs_indicate,
2317*4882a593Smuzhiyun fwstats->txs_discard,
2318*4882a593Smuzhiyun fwstats->txs_supp_core,
2319*4882a593Smuzhiyun fwstats->txs_supp_ps,
2320*4882a593Smuzhiyun fwstats->txs_tossed,
2321*4882a593Smuzhiyun fwstats->txs_host_tossed,
2322*4882a593Smuzhiyun fwstats->bus_flow_block,
2323*4882a593Smuzhiyun fwstats->fws_flow_block,
2324*4882a593Smuzhiyun fwstats->send_pkts[0], fwstats->send_pkts[1],
2325*4882a593Smuzhiyun fwstats->send_pkts[2], fwstats->send_pkts[3],
2326*4882a593Smuzhiyun fwstats->send_pkts[4],
2327*4882a593Smuzhiyun fwstats->requested_sent[0],
2328*4882a593Smuzhiyun fwstats->requested_sent[1],
2329*4882a593Smuzhiyun fwstats->requested_sent[2],
2330*4882a593Smuzhiyun fwstats->requested_sent[3],
2331*4882a593Smuzhiyun fwstats->requested_sent[4]);
2332*4882a593Smuzhiyun
2333*4882a593Smuzhiyun return 0;
2334*4882a593Smuzhiyun }
2335*4882a593Smuzhiyun #else
brcmf_debugfs_fws_stats_read(struct seq_file * seq,void * data)2336*4882a593Smuzhiyun static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
2337*4882a593Smuzhiyun {
2338*4882a593Smuzhiyun return 0;
2339*4882a593Smuzhiyun }
2340*4882a593Smuzhiyun #endif
2341*4882a593Smuzhiyun
brcmf_fws_attach(struct brcmf_pub * drvr)2342*4882a593Smuzhiyun struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr)
2343*4882a593Smuzhiyun {
2344*4882a593Smuzhiyun struct brcmf_fws_info *fws;
2345*4882a593Smuzhiyun struct brcmf_if *ifp;
2346*4882a593Smuzhiyun u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
2347*4882a593Smuzhiyun int rc;
2348*4882a593Smuzhiyun u32 mode;
2349*4882a593Smuzhiyun
2350*4882a593Smuzhiyun fws = kzalloc(sizeof(*fws), GFP_KERNEL);
2351*4882a593Smuzhiyun if (!fws) {
2352*4882a593Smuzhiyun rc = -ENOMEM;
2353*4882a593Smuzhiyun goto fail;
2354*4882a593Smuzhiyun }
2355*4882a593Smuzhiyun
2356*4882a593Smuzhiyun spin_lock_init(&fws->spinlock);
2357*4882a593Smuzhiyun
2358*4882a593Smuzhiyun /* store drvr reference */
2359*4882a593Smuzhiyun fws->drvr = drvr;
2360*4882a593Smuzhiyun fws->fcmode = drvr->settings->fcmode;
2361*4882a593Smuzhiyun
2362*4882a593Smuzhiyun if (!drvr->bus_if->always_use_fws_queue &&
2363*4882a593Smuzhiyun (fws->fcmode == BRCMF_FWS_FCMODE_NONE)) {
2364*4882a593Smuzhiyun fws->avoid_queueing = true;
2365*4882a593Smuzhiyun brcmf_dbg(INFO, "FWS queueing will be avoided\n");
2366*4882a593Smuzhiyun return fws;
2367*4882a593Smuzhiyun }
2368*4882a593Smuzhiyun
2369*4882a593Smuzhiyun fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
2370*4882a593Smuzhiyun if (fws->fws_wq == NULL) {
2371*4882a593Smuzhiyun bphy_err(drvr, "workqueue creation failed\n");
2372*4882a593Smuzhiyun rc = -EBADF;
2373*4882a593Smuzhiyun goto fail;
2374*4882a593Smuzhiyun }
2375*4882a593Smuzhiyun INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
2376*4882a593Smuzhiyun
2377*4882a593Smuzhiyun /* enable firmware signalling if fcmode active */
2378*4882a593Smuzhiyun if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
2379*4882a593Smuzhiyun tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
2380*4882a593Smuzhiyun BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
2381*4882a593Smuzhiyun BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
2382*4882a593Smuzhiyun BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;
2383*4882a593Smuzhiyun
2384*4882a593Smuzhiyun rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
2385*4882a593Smuzhiyun brcmf_fws_notify_credit_map);
2386*4882a593Smuzhiyun if (rc < 0) {
2387*4882a593Smuzhiyun bphy_err(drvr, "register credit map handler failed\n");
2388*4882a593Smuzhiyun goto fail;
2389*4882a593Smuzhiyun }
2390*4882a593Smuzhiyun rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT,
2391*4882a593Smuzhiyun brcmf_fws_notify_bcmc_credit_support);
2392*4882a593Smuzhiyun if (rc < 0) {
2393*4882a593Smuzhiyun bphy_err(drvr, "register bcmc credit handler failed\n");
2394*4882a593Smuzhiyun brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
2395*4882a593Smuzhiyun goto fail;
2396*4882a593Smuzhiyun }
2397*4882a593Smuzhiyun
2398*4882a593Smuzhiyun /* Setting the iovar may fail if feature is unsupported
2399*4882a593Smuzhiyun * so leave the rc as is so driver initialization can
2400*4882a593Smuzhiyun * continue. Set mode back to none indicating not enabled.
2401*4882a593Smuzhiyun */
2402*4882a593Smuzhiyun fws->fw_signals = true;
2403*4882a593Smuzhiyun ifp = brcmf_get_ifp(drvr, 0);
2404*4882a593Smuzhiyun if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
2405*4882a593Smuzhiyun bphy_err(drvr, "failed to set bdcv2 tlv signaling\n");
2406*4882a593Smuzhiyun fws->fcmode = BRCMF_FWS_FCMODE_NONE;
2407*4882a593Smuzhiyun fws->fw_signals = false;
2408*4882a593Smuzhiyun }
2409*4882a593Smuzhiyun
2410*4882a593Smuzhiyun if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
2411*4882a593Smuzhiyun brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
2412*4882a593Smuzhiyun
2413*4882a593Smuzhiyun /* Enable seq number reuse, if supported */
2414*4882a593Smuzhiyun if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
2415*4882a593Smuzhiyun if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
2416*4882a593Smuzhiyun mode = 0;
2417*4882a593Smuzhiyun BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
2418*4882a593Smuzhiyun if (brcmf_fil_iovar_int_set(ifp,
2419*4882a593Smuzhiyun "wlfc_mode", mode) == 0) {
2420*4882a593Smuzhiyun BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
2421*4882a593Smuzhiyun }
2422*4882a593Smuzhiyun }
2423*4882a593Smuzhiyun }
2424*4882a593Smuzhiyun
2425*4882a593Smuzhiyun brcmf_fws_hanger_init(&fws->hanger);
2426*4882a593Smuzhiyun brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
2427*4882a593Smuzhiyun brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
2428*4882a593Smuzhiyun brcmf_dbg(INFO, "added %s\n", fws->desc.other.name);
2429*4882a593Smuzhiyun brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
2430*4882a593Smuzhiyun BRCMF_FWS_PSQ_LEN);
2431*4882a593Smuzhiyun
2432*4882a593Smuzhiyun brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
2433*4882a593Smuzhiyun fws->fw_signals ? "enabled" : "disabled", tlv);
2434*4882a593Smuzhiyun return fws;
2435*4882a593Smuzhiyun
2436*4882a593Smuzhiyun fail:
2437*4882a593Smuzhiyun brcmf_fws_detach(fws);
2438*4882a593Smuzhiyun return ERR_PTR(rc);
2439*4882a593Smuzhiyun }
2440*4882a593Smuzhiyun
brcmf_fws_detach(struct brcmf_fws_info * fws)2441*4882a593Smuzhiyun void brcmf_fws_detach(struct brcmf_fws_info *fws)
2442*4882a593Smuzhiyun {
2443*4882a593Smuzhiyun if (!fws)
2444*4882a593Smuzhiyun return;
2445*4882a593Smuzhiyun
2446*4882a593Smuzhiyun if (fws->fws_wq)
2447*4882a593Smuzhiyun destroy_workqueue(fws->fws_wq);
2448*4882a593Smuzhiyun
2449*4882a593Smuzhiyun /* cleanup */
2450*4882a593Smuzhiyun brcmf_fws_lock(fws);
2451*4882a593Smuzhiyun brcmf_fws_cleanup(fws, -1);
2452*4882a593Smuzhiyun brcmf_fws_unlock(fws);
2453*4882a593Smuzhiyun
2454*4882a593Smuzhiyun /* free top structure */
2455*4882a593Smuzhiyun kfree(fws);
2456*4882a593Smuzhiyun }
2457*4882a593Smuzhiyun
brcmf_fws_debugfs_create(struct brcmf_pub * drvr)2458*4882a593Smuzhiyun void brcmf_fws_debugfs_create(struct brcmf_pub *drvr)
2459*4882a593Smuzhiyun {
2460*4882a593Smuzhiyun /* create debugfs file for statistics */
2461*4882a593Smuzhiyun brcmf_debugfs_add_entry(drvr, "fws_stats",
2462*4882a593Smuzhiyun brcmf_debugfs_fws_stats_read);
2463*4882a593Smuzhiyun }
2464*4882a593Smuzhiyun
brcmf_fws_queue_skbs(struct brcmf_fws_info * fws)2465*4882a593Smuzhiyun bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws)
2466*4882a593Smuzhiyun {
2467*4882a593Smuzhiyun return !fws->avoid_queueing;
2468*4882a593Smuzhiyun }
2469*4882a593Smuzhiyun
brcmf_fws_fc_active(struct brcmf_fws_info * fws)2470*4882a593Smuzhiyun bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
2471*4882a593Smuzhiyun {
2472*4882a593Smuzhiyun if (!fws->creditmap_received)
2473*4882a593Smuzhiyun return false;
2474*4882a593Smuzhiyun
2475*4882a593Smuzhiyun return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
2476*4882a593Smuzhiyun }
2477*4882a593Smuzhiyun
brcmf_fws_bustxfail(struct brcmf_fws_info * fws,struct sk_buff * skb)2478*4882a593Smuzhiyun void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
2479*4882a593Smuzhiyun {
2480*4882a593Smuzhiyun u32 hslot;
2481*4882a593Smuzhiyun
2482*4882a593Smuzhiyun if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
2483*4882a593Smuzhiyun brcmu_pkt_buf_free_skb(skb);
2484*4882a593Smuzhiyun return;
2485*4882a593Smuzhiyun }
2486*4882a593Smuzhiyun brcmf_fws_lock(fws);
2487*4882a593Smuzhiyun hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
2488*4882a593Smuzhiyun brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0,
2489*4882a593Smuzhiyun 1);
2490*4882a593Smuzhiyun brcmf_fws_unlock(fws);
2491*4882a593Smuzhiyun }
2492*4882a593Smuzhiyun
brcmf_fws_bus_blocked(struct brcmf_pub * drvr,bool flow_blocked)2493*4882a593Smuzhiyun void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
2494*4882a593Smuzhiyun {
2495*4882a593Smuzhiyun struct brcmf_fws_info *fws = drvr_to_fws(drvr);
2496*4882a593Smuzhiyun struct brcmf_if *ifp;
2497*4882a593Smuzhiyun int i;
2498*4882a593Smuzhiyun
2499*4882a593Smuzhiyun if (fws->avoid_queueing) {
2500*4882a593Smuzhiyun for (i = 0; i < BRCMF_MAX_IFS; i++) {
2501*4882a593Smuzhiyun ifp = drvr->iflist[i];
2502*4882a593Smuzhiyun if (!ifp || !ifp->ndev)
2503*4882a593Smuzhiyun continue;
2504*4882a593Smuzhiyun brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW,
2505*4882a593Smuzhiyun flow_blocked);
2506*4882a593Smuzhiyun }
2507*4882a593Smuzhiyun } else {
2508*4882a593Smuzhiyun fws->bus_flow_blocked = flow_blocked;
2509*4882a593Smuzhiyun if (!flow_blocked)
2510*4882a593Smuzhiyun brcmf_fws_schedule_deq(fws);
2511*4882a593Smuzhiyun else
2512*4882a593Smuzhiyun fws->stats.bus_flow_block++;
2513*4882a593Smuzhiyun }
2514*4882a593Smuzhiyun }
2515