xref: /openbmc/linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo  * Copyright (c) 2010 Broadcom Corporation
405491d2cSKalle Valo  */
505491d2cSKalle Valo #include <linux/types.h>
605491d2cSKalle Valo #include <linux/module.h>
705491d2cSKalle Valo #include <linux/if_ether.h>
805491d2cSKalle Valo #include <linux/spinlock.h>
905491d2cSKalle Valo #include <linux/skbuff.h>
1005491d2cSKalle Valo #include <linux/netdevice.h>
1105491d2cSKalle Valo #include <linux/etherdevice.h>
1205491d2cSKalle Valo #include <linux/err.h>
1305491d2cSKalle Valo #include <linux/jiffies.h>
1405491d2cSKalle Valo #include <net/cfg80211.h>
1505491d2cSKalle Valo 
1605491d2cSKalle Valo #include <brcmu_utils.h>
1705491d2cSKalle Valo #include <brcmu_wifi.h>
1805491d2cSKalle Valo #include "core.h"
1905491d2cSKalle Valo #include "debug.h"
2005491d2cSKalle Valo #include "bus.h"
2105491d2cSKalle Valo #include "fwil.h"
2205491d2cSKalle Valo #include "fwil_types.h"
2305491d2cSKalle Valo #include "fweh.h"
2405491d2cSKalle Valo #include "fwsignal.h"
2505491d2cSKalle Valo #include "p2p.h"
2605491d2cSKalle Valo #include "cfg80211.h"
2705491d2cSKalle Valo #include "proto.h"
28acf8ac41SArend Van Spriel #include "bcdc.h"
297d34b056SHante Meuleman #include "common.h"
3005491d2cSKalle Valo 
3105491d2cSKalle Valo /**
3205491d2cSKalle Valo  * DOC: Firmware Signalling
3305491d2cSKalle Valo  *
3405491d2cSKalle Valo  * Firmware can send signals to host and vice versa, which are passed in the
3505491d2cSKalle Valo  * data packets using TLV based header. This signalling layer is on top of the
3605491d2cSKalle Valo  * BDC bus protocol layer.
3705491d2cSKalle Valo  */
3805491d2cSKalle Valo 
3905491d2cSKalle Valo /*
4005491d2cSKalle Valo  * single definition for firmware-driver flow control tlv's.
4105491d2cSKalle Valo  *
4205491d2cSKalle Valo  * each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length).
4305491d2cSKalle Valo  * A length value 0 indicates variable length tlv.
4405491d2cSKalle Valo  */
4505491d2cSKalle Valo #define BRCMF_FWS_TLV_DEFLIST \
4605491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \
4705491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \
4805491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \
4905491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \
5005491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \
5105491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(MACDESC_ADD,	6, 8) \
5205491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \
5305491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \
5405491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \
5505491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \
5605491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \
5705491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \
5805491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \
5905491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \
6005491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \
6105491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \
6205491d2cSKalle Valo 	BRCMF_FWS_TLV_DEF(FILLER, 255, 0)
6305491d2cSKalle Valo 
6405491d2cSKalle Valo /*
6505491d2cSKalle Valo  * enum brcmf_fws_tlv_type - definition of tlv identifiers.
6605491d2cSKalle Valo  */
6705491d2cSKalle Valo #define BRCMF_FWS_TLV_DEF(name, id, len) \
6805491d2cSKalle Valo 	BRCMF_FWS_TYPE_ ## name =  id,
6905491d2cSKalle Valo enum brcmf_fws_tlv_type {
7005491d2cSKalle Valo 	BRCMF_FWS_TLV_DEFLIST
7105491d2cSKalle Valo 	BRCMF_FWS_TYPE_INVALID
7205491d2cSKalle Valo };
7305491d2cSKalle Valo #undef BRCMF_FWS_TLV_DEF
7405491d2cSKalle Valo 
7505491d2cSKalle Valo /*
7605491d2cSKalle Valo  * enum brcmf_fws_tlv_len - definition of tlv lengths.
7705491d2cSKalle Valo  */
7805491d2cSKalle Valo #define BRCMF_FWS_TLV_DEF(name, id, len) \
7905491d2cSKalle Valo 	BRCMF_FWS_TYPE_ ## name ## _LEN = (len),
8005491d2cSKalle Valo enum brcmf_fws_tlv_len {
8105491d2cSKalle Valo 	BRCMF_FWS_TLV_DEFLIST
8205491d2cSKalle Valo };
8305491d2cSKalle Valo #undef BRCMF_FWS_TLV_DEF
8405491d2cSKalle Valo 
85bbd1f932SArend van Spriel /* AMPDU rx reordering definitions */
86bbd1f932SArend van Spriel #define BRCMF_RXREORDER_FLOWID_OFFSET		0
87bbd1f932SArend van Spriel #define BRCMF_RXREORDER_MAXIDX_OFFSET		2
88bbd1f932SArend van Spriel #define BRCMF_RXREORDER_FLAGS_OFFSET		4
89bbd1f932SArend van Spriel #define BRCMF_RXREORDER_CURIDX_OFFSET		6
90bbd1f932SArend van Spriel #define BRCMF_RXREORDER_EXPIDX_OFFSET		8
91bbd1f932SArend van Spriel 
92bbd1f932SArend van Spriel #define BRCMF_RXREORDER_DEL_FLOW		0x01
93bbd1f932SArend van Spriel #define BRCMF_RXREORDER_FLUSH_ALL		0x02
94bbd1f932SArend van Spriel #define BRCMF_RXREORDER_CURIDX_VALID		0x04
95bbd1f932SArend van Spriel #define BRCMF_RXREORDER_EXPIDX_VALID		0x08
96bbd1f932SArend van Spriel #define BRCMF_RXREORDER_NEW_HOLE		0x10
97bbd1f932SArend van Spriel 
9805491d2cSKalle Valo #ifdef DEBUG
9905491d2cSKalle Valo /*
10005491d2cSKalle Valo  * brcmf_fws_tlv_names - array of tlv names.
10105491d2cSKalle Valo  */
10205491d2cSKalle Valo #define BRCMF_FWS_TLV_DEF(name, id, len) \
10305491d2cSKalle Valo 	{ id, #name },
10405491d2cSKalle Valo static struct {
10505491d2cSKalle Valo 	enum brcmf_fws_tlv_type id;
10605491d2cSKalle Valo 	const char *name;
10705491d2cSKalle Valo } brcmf_fws_tlv_names[] = {
10805491d2cSKalle Valo 	BRCMF_FWS_TLV_DEFLIST
10905491d2cSKalle Valo };
11005491d2cSKalle Valo #undef BRCMF_FWS_TLV_DEF
11105491d2cSKalle Valo 
11205491d2cSKalle Valo 
brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)11305491d2cSKalle Valo static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
11405491d2cSKalle Valo {
11505491d2cSKalle Valo 	int i;
11605491d2cSKalle Valo 
11705491d2cSKalle Valo 	for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++)
11805491d2cSKalle Valo 		if (brcmf_fws_tlv_names[i].id == id)
11905491d2cSKalle Valo 			return brcmf_fws_tlv_names[i].name;
12005491d2cSKalle Valo 
12105491d2cSKalle Valo 	return "INVALID";
12205491d2cSKalle Valo }
12305491d2cSKalle Valo #else
brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)12405491d2cSKalle Valo static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
12505491d2cSKalle Valo {
12605491d2cSKalle Valo 	return "NODEBUG";
12705491d2cSKalle Valo }
12805491d2cSKalle Valo #endif /* DEBUG */
12905491d2cSKalle Valo 
13005491d2cSKalle Valo /*
13105491d2cSKalle Valo  * The PKTTAG tlv has additional bytes when firmware-signalling
13205491d2cSKalle Valo  * mode has REUSESEQ flag set.
13305491d2cSKalle Valo  */
13405491d2cSKalle Valo #define BRCMF_FWS_TYPE_SEQ_LEN				2
13505491d2cSKalle Valo 
13605491d2cSKalle Valo /*
13705491d2cSKalle Valo  * flags used to enable tlv signalling from firmware.
13805491d2cSKalle Valo  */
13905491d2cSKalle Valo #define BRCMF_FWS_FLAGS_RSSI_SIGNALS			0x0001
14005491d2cSKalle Valo #define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS			0x0002
14105491d2cSKalle Valo #define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS		0x0004
14205491d2cSKalle Valo #define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE	0x0008
14305491d2cSKalle Valo #define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE	0x0010
14405491d2cSKalle Valo #define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE		0x0020
14505491d2cSKalle Valo #define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE		0x0040
14605491d2cSKalle Valo 
14705491d2cSKalle Valo #define BRCMF_FWS_MAC_DESC_TABLE_SIZE			32
14805491d2cSKalle Valo #define BRCMF_FWS_MAC_DESC_ID_INVALID			0xff
14905491d2cSKalle Valo 
15005491d2cSKalle Valo #define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF			0
15105491d2cSKalle Valo #define BRCMF_FWS_HOSTIF_FLOWSTATE_ON			1
15205491d2cSKalle Valo #define BRCMF_FWS_FLOWCONTROL_HIWATER			128
15305491d2cSKalle Valo #define BRCMF_FWS_FLOWCONTROL_LOWATER			64
15405491d2cSKalle Valo 
15505491d2cSKalle Valo #define BRCMF_FWS_PSQ_PREC_COUNT		((BRCMF_FWS_FIFO_COUNT + 1) * 2)
15605491d2cSKalle Valo #define BRCMF_FWS_PSQ_LEN				256
15705491d2cSKalle Valo 
15805491d2cSKalle Valo #define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST			0x01
15905491d2cSKalle Valo #define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED		0x02
16005491d2cSKalle Valo 
16105491d2cSKalle Valo #define BRCMF_FWS_RET_OK_NOSCHEDULE			0
16205491d2cSKalle Valo #define BRCMF_FWS_RET_OK_SCHEDULE			1
16305491d2cSKalle Valo 
16405491d2cSKalle Valo #define BRCMF_FWS_MODE_REUSESEQ_SHIFT			3	/* seq reuse */
16505491d2cSKalle Valo #define BRCMF_FWS_MODE_SET_REUSESEQ(x, val)	((x) = \
16605491d2cSKalle Valo 		((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \
16705491d2cSKalle Valo 		(((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT))
16805491d2cSKalle Valo #define BRCMF_FWS_MODE_GET_REUSESEQ(x)	\
16905491d2cSKalle Valo 		(((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1)
17005491d2cSKalle Valo 
17105491d2cSKalle Valo /**
17205491d2cSKalle Valo  * enum brcmf_fws_skb_state - indicates processing state of skb.
17305491d2cSKalle Valo  *
17405491d2cSKalle Valo  * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver.
17505491d2cSKalle Valo  * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue.
17605491d2cSKalle Valo  * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware.
17705491d2cSKalle Valo  * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info.
17805491d2cSKalle Valo  */
17905491d2cSKalle Valo enum brcmf_fws_skb_state {
18005491d2cSKalle Valo 	BRCMF_FWS_SKBSTATE_NEW,
18105491d2cSKalle Valo 	BRCMF_FWS_SKBSTATE_DELAYED,
18205491d2cSKalle Valo 	BRCMF_FWS_SKBSTATE_SUPPRESSED,
18305491d2cSKalle Valo 	BRCMF_FWS_SKBSTATE_TIM
18405491d2cSKalle Valo };
18505491d2cSKalle Valo 
18605491d2cSKalle Valo /**
18705491d2cSKalle Valo  * struct brcmf_skbuff_cb - control buffer associated with skbuff.
18805491d2cSKalle Valo  *
18905491d2cSKalle Valo  * @bus_flags: 2 bytes reserved for bus specific parameters
19005491d2cSKalle Valo  * @if_flags: holds interface index and packet related flags.
19105491d2cSKalle Valo  * @htod: host to device packet identifier (used in PKTTAG tlv).
19205491d2cSKalle Valo  * @htod_seq: this 16-bit is original seq number for every suppress packet.
19305491d2cSKalle Valo  * @state: transmit state of the packet.
19405491d2cSKalle Valo  * @mac: descriptor related to destination for this packet.
19505491d2cSKalle Valo  *
19605491d2cSKalle Valo  * This information is stored in control buffer struct sk_buff::cb, which
19705491d2cSKalle Valo  * provides 48 bytes of storage so this structure should not exceed that.
19805491d2cSKalle Valo  */
19905491d2cSKalle Valo struct brcmf_skbuff_cb {
20005491d2cSKalle Valo 	u16 bus_flags;
20105491d2cSKalle Valo 	u16 if_flags;
20205491d2cSKalle Valo 	u32 htod;
20305491d2cSKalle Valo 	u16 htod_seq;
20405491d2cSKalle Valo 	enum brcmf_fws_skb_state state;
20505491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *mac;
20605491d2cSKalle Valo };
20705491d2cSKalle Valo 
20805491d2cSKalle Valo /*
20905491d2cSKalle Valo  * macro casting skbuff control buffer to struct brcmf_skbuff_cb.
21005491d2cSKalle Valo  */
21105491d2cSKalle Valo #define brcmf_skbcb(skb)	((struct brcmf_skbuff_cb *)((skb)->cb))
21205491d2cSKalle Valo 
21305491d2cSKalle Valo /*
21405491d2cSKalle Valo  * sk_buff control if flags
21505491d2cSKalle Valo  *
21605491d2cSKalle Valo  *	b[11]  - packet sent upon firmware request.
21705491d2cSKalle Valo  *	b[10]  - packet only contains signalling data.
21805491d2cSKalle Valo  *	b[9]   - packet is a tx packet.
21905491d2cSKalle Valo  *	b[8]   - packet used requested credit
22005491d2cSKalle Valo  *	b[7]   - interface in AP mode.
22105491d2cSKalle Valo  *	b[3:0] - interface index.
22205491d2cSKalle Valo  */
22305491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK	0x0800
22405491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT	11
22505491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK	0x0400
22605491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT	10
22705491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK        0x0200
22805491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT	9
22905491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK	0x0100
23005491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT	8
23105491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_IF_AP_MASK		0x0080
23205491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT		7
23305491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_INDEX_MASK		0x000f
23405491d2cSKalle Valo #define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT		0
23505491d2cSKalle Valo 
23605491d2cSKalle Valo #define brcmf_skb_if_flags_set_field(skb, field, value) \
23705491d2cSKalle Valo 	brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \
23805491d2cSKalle Valo 			BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
23905491d2cSKalle Valo 			BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value))
24005491d2cSKalle Valo #define brcmf_skb_if_flags_get_field(skb, field) \
24105491d2cSKalle Valo 	brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \
24205491d2cSKalle Valo 			BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
24305491d2cSKalle Valo 			BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT)
24405491d2cSKalle Valo 
24505491d2cSKalle Valo /*
24605491d2cSKalle Valo  * sk_buff control packet identifier
24705491d2cSKalle Valo  *
24805491d2cSKalle Valo  * 32-bit packet identifier used in PKTTAG tlv from host to dongle.
24905491d2cSKalle Valo  *
25005491d2cSKalle Valo  * - Generated at the host (e.g. dhd)
25105491d2cSKalle Valo  * - Seen as a generic sequence number by firmware except for the flags field.
25205491d2cSKalle Valo  *
25305491d2cSKalle Valo  * Generation	: b[31]	=> generation number for this packet [host->fw]
25405491d2cSKalle Valo  *			   OR, current generation number [fw->host]
25505491d2cSKalle Valo  * Flags	: b[30:27] => command, status flags
25605491d2cSKalle Valo  * FIFO-AC	: b[26:24] => AC-FIFO id
25705491d2cSKalle Valo  * h-slot	: b[23:8] => hanger-slot
25805491d2cSKalle Valo  * freerun	: b[7:0] => A free running counter
25905491d2cSKalle Valo  */
26005491d2cSKalle Valo #define BRCMF_SKB_HTOD_TAG_GENERATION_MASK		0x80000000
26105491d2cSKalle Valo #define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT		31
26205491d2cSKalle Valo #define BRCMF_SKB_HTOD_TAG_FLAGS_MASK			0x78000000
26305491d2cSKalle Valo #define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT			27
26405491d2cSKalle Valo #define BRCMF_SKB_HTOD_TAG_FIFO_MASK			0x07000000
26505491d2cSKalle Valo #define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT			24
26605491d2cSKalle Valo #define BRCMF_SKB_HTOD_TAG_HSLOT_MASK			0x00ffff00
26705491d2cSKalle Valo #define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT			8
26805491d2cSKalle Valo #define BRCMF_SKB_HTOD_TAG_FREERUN_MASK			0x000000ff
26905491d2cSKalle Valo #define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT		0
27005491d2cSKalle Valo 
27105491d2cSKalle Valo #define brcmf_skb_htod_tag_set_field(skb, field, value) \
27205491d2cSKalle Valo 	brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \
27305491d2cSKalle Valo 			BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
27405491d2cSKalle Valo 			BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value))
27505491d2cSKalle Valo #define brcmf_skb_htod_tag_get_field(skb, field) \
27605491d2cSKalle Valo 	brcmu_maskget32(brcmf_skbcb(skb)->htod, \
27705491d2cSKalle Valo 			BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
27805491d2cSKalle Valo 			BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT)
27905491d2cSKalle Valo 
28005491d2cSKalle Valo #define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK			0x2000
28105491d2cSKalle Valo #define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT			13
28205491d2cSKalle Valo #define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK			0x1000
28305491d2cSKalle Valo #define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT		12
28405491d2cSKalle Valo #define BRCMF_SKB_HTOD_SEQ_NR_MASK			0x0fff
28505491d2cSKalle Valo #define BRCMF_SKB_HTOD_SEQ_NR_SHIFT			0
28605491d2cSKalle Valo 
28705491d2cSKalle Valo #define brcmf_skb_htod_seq_set_field(skb, field, value) \
28805491d2cSKalle Valo 	brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \
28905491d2cSKalle Valo 			BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
29005491d2cSKalle Valo 			BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value))
29105491d2cSKalle Valo #define brcmf_skb_htod_seq_get_field(skb, field) \
29205491d2cSKalle Valo 	brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \
29305491d2cSKalle Valo 			BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
29405491d2cSKalle Valo 			BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT)
29505491d2cSKalle Valo 
29605491d2cSKalle Valo #define BRCMF_FWS_TXSTAT_GENERATION_MASK	0x80000000
29705491d2cSKalle Valo #define BRCMF_FWS_TXSTAT_GENERATION_SHIFT	31
29805491d2cSKalle Valo #define BRCMF_FWS_TXSTAT_FLAGS_MASK		0x78000000
29905491d2cSKalle Valo #define BRCMF_FWS_TXSTAT_FLAGS_SHIFT		27
30005491d2cSKalle Valo #define BRCMF_FWS_TXSTAT_FIFO_MASK		0x07000000
30105491d2cSKalle Valo #define BRCMF_FWS_TXSTAT_FIFO_SHIFT		24
30205491d2cSKalle Valo #define BRCMF_FWS_TXSTAT_HSLOT_MASK		0x00FFFF00
30305491d2cSKalle Valo #define BRCMF_FWS_TXSTAT_HSLOT_SHIFT		8
30405491d2cSKalle Valo #define BRCMF_FWS_TXSTAT_FREERUN_MASK		0x000000FF
30505491d2cSKalle Valo #define BRCMF_FWS_TXSTAT_FREERUN_SHIFT		0
30605491d2cSKalle Valo 
30705491d2cSKalle Valo #define brcmf_txstatus_get_field(txs, field) \
30805491d2cSKalle Valo 	brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
30905491d2cSKalle Valo 			BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)
31005491d2cSKalle Valo 
31105491d2cSKalle Valo /* How long to defer borrowing in jiffies */
31205491d2cSKalle Valo #define BRCMF_FWS_BORROW_DEFER_PERIOD		(HZ / 10)
31305491d2cSKalle Valo 
31405491d2cSKalle Valo 
31505491d2cSKalle Valo /**
31605491d2cSKalle Valo  * enum brcmf_fws_txstatus - txstatus flag values.
31705491d2cSKalle Valo  *
31805491d2cSKalle Valo  * @BRCMF_FWS_TXSTATUS_DISCARD:
31905491d2cSKalle Valo  *	host is free to discard the packet.
32005491d2cSKalle Valo  * @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS:
32105491d2cSKalle Valo  *	802.11 core suppressed the packet.
32205491d2cSKalle Valo  * @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS:
32305491d2cSKalle Valo  *	firmware suppress the packet as device is already in PS mode.
32405491d2cSKalle Valo  * @BRCMF_FWS_TXSTATUS_FW_TOSSED:
32505491d2cSKalle Valo  *	firmware tossed the packet.
326d843246eSChung-Hsien Hsu  * @BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK:
327d843246eSChung-Hsien Hsu  *	firmware tossed the packet after retries.
328d843246eSChung-Hsien Hsu  * @BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED:
329d843246eSChung-Hsien Hsu  *	firmware wrongly reported suppressed previously, now fixing to acked.
33005491d2cSKalle Valo  * @BRCMF_FWS_TXSTATUS_HOST_TOSSED:
33105491d2cSKalle Valo  *	host tossed the packet.
33205491d2cSKalle Valo  */
33305491d2cSKalle Valo enum brcmf_fws_txstatus {
33405491d2cSKalle Valo 	BRCMF_FWS_TXSTATUS_DISCARD,
33505491d2cSKalle Valo 	BRCMF_FWS_TXSTATUS_CORE_SUPPRESS,
33605491d2cSKalle Valo 	BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS,
33705491d2cSKalle Valo 	BRCMF_FWS_TXSTATUS_FW_TOSSED,
338d843246eSChung-Hsien Hsu 	BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK,
339d843246eSChung-Hsien Hsu 	BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED,
34005491d2cSKalle Valo 	BRCMF_FWS_TXSTATUS_HOST_TOSSED
34105491d2cSKalle Valo };
34205491d2cSKalle Valo 
34305491d2cSKalle Valo enum brcmf_fws_fcmode {
34405491d2cSKalle Valo 	BRCMF_FWS_FCMODE_NONE,
34505491d2cSKalle Valo 	BRCMF_FWS_FCMODE_IMPLIED_CREDIT,
34605491d2cSKalle Valo 	BRCMF_FWS_FCMODE_EXPLICIT_CREDIT
34705491d2cSKalle Valo };
34805491d2cSKalle Valo 
34905491d2cSKalle Valo enum brcmf_fws_mac_desc_state {
35005491d2cSKalle Valo 	BRCMF_FWS_STATE_OPEN = 1,
35105491d2cSKalle Valo 	BRCMF_FWS_STATE_CLOSE
35205491d2cSKalle Valo };
35305491d2cSKalle Valo 
35405491d2cSKalle Valo /**
35505491d2cSKalle Valo  * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface
35605491d2cSKalle Valo  *
357e769ab53SLee Jones  * @name: name of the descriptor.
35805491d2cSKalle Valo  * @occupied: slot is in use.
35905491d2cSKalle Valo  * @mac_handle: handle for mac entry determined by firmware.
36005491d2cSKalle Valo  * @interface_id: interface index.
36105491d2cSKalle Valo  * @state: current state.
36205491d2cSKalle Valo  * @suppressed: mac entry is suppressed.
36305491d2cSKalle Valo  * @generation: generation bit.
36405491d2cSKalle Valo  * @ac_bitmap: ac queue bitmap.
36505491d2cSKalle Valo  * @requested_credit: credits requested by firmware.
366e769ab53SLee Jones  * @requested_packet: packet requested by firmware.
36705491d2cSKalle Valo  * @ea: ethernet address.
36805491d2cSKalle Valo  * @seq: per-node free-running sequence.
36905491d2cSKalle Valo  * @psq: power-save queue.
37005491d2cSKalle Valo  * @transit_count: packet in transit to firmware.
371e769ab53SLee Jones  * @suppr_transit_count: suppressed packet in transit to firmware.
372e769ab53SLee Jones  * @send_tim_signal: if set tim signal will be sent.
373e769ab53SLee Jones  * @traffic_pending_bmp: traffic pending bitmap.
374e769ab53SLee Jones  * @traffic_lastreported_bmp: traffic last reported bitmap.
37505491d2cSKalle Valo  */
37605491d2cSKalle Valo struct brcmf_fws_mac_descriptor {
37705491d2cSKalle Valo 	char name[16];
37805491d2cSKalle Valo 	u8 occupied;
37905491d2cSKalle Valo 	u8 mac_handle;
38005491d2cSKalle Valo 	u8 interface_id;
38105491d2cSKalle Valo 	u8 state;
38205491d2cSKalle Valo 	bool suppressed;
38305491d2cSKalle Valo 	u8 generation;
38405491d2cSKalle Valo 	u8 ac_bitmap;
38505491d2cSKalle Valo 	u8 requested_credit;
38605491d2cSKalle Valo 	u8 requested_packet;
38705491d2cSKalle Valo 	u8 ea[ETH_ALEN];
38805491d2cSKalle Valo 	u8 seq[BRCMF_FWS_FIFO_COUNT];
38905491d2cSKalle Valo 	struct pktq psq;
39005491d2cSKalle Valo 	int transit_count;
39105491d2cSKalle Valo 	int suppr_transit_count;
39205491d2cSKalle Valo 	bool send_tim_signal;
39305491d2cSKalle Valo 	u8 traffic_pending_bmp;
39405491d2cSKalle Valo 	u8 traffic_lastreported_bmp;
39505491d2cSKalle Valo };
39605491d2cSKalle Valo 
3972bc50d88SMadhan Mohan R #define BRCMF_FWS_HANGER_MAXITEMS	3072
398683608bdSRaveendran Somu #define BRCMF_BORROW_RATIO			3
39905491d2cSKalle Valo 
40005491d2cSKalle Valo /**
40105491d2cSKalle Valo  * enum brcmf_fws_hanger_item_state - state of hanger item.
40205491d2cSKalle Valo  *
40305491d2cSKalle Valo  * @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use.
40405491d2cSKalle Valo  * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use.
40505491d2cSKalle Valo  * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed.
40605491d2cSKalle Valo  */
40705491d2cSKalle Valo enum brcmf_fws_hanger_item_state {
40805491d2cSKalle Valo 	BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1,
40905491d2cSKalle Valo 	BRCMF_FWS_HANGER_ITEM_STATE_INUSE,
41005491d2cSKalle Valo 	BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED
41105491d2cSKalle Valo };
41205491d2cSKalle Valo 
41305491d2cSKalle Valo 
41405491d2cSKalle Valo /**
41505491d2cSKalle Valo  * struct brcmf_fws_hanger_item - single entry for tx pending packet.
41605491d2cSKalle Valo  *
41705491d2cSKalle Valo  * @state: entry is either free or occupied.
41805491d2cSKalle Valo  * @pkt: packet itself.
41905491d2cSKalle Valo  */
42005491d2cSKalle Valo struct brcmf_fws_hanger_item {
42105491d2cSKalle Valo 	enum brcmf_fws_hanger_item_state state;
42205491d2cSKalle Valo 	struct sk_buff *pkt;
42305491d2cSKalle Valo };
42405491d2cSKalle Valo 
42505491d2cSKalle Valo /**
42605491d2cSKalle Valo  * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus.
42705491d2cSKalle Valo  *
42805491d2cSKalle Valo  * @pushed: packets pushed to await txstatus.
42905491d2cSKalle Valo  * @popped: packets popped upon handling txstatus.
43005491d2cSKalle Valo  * @failed_to_push: packets that could not be pushed.
43105491d2cSKalle Valo  * @failed_to_pop: packets that could not be popped.
43205491d2cSKalle Valo  * @failed_slotfind: packets for which failed to find an entry.
43305491d2cSKalle Valo  * @slot_pos: last returned item index for a free entry.
43405491d2cSKalle Valo  * @items: array of hanger items.
43505491d2cSKalle Valo  */
43605491d2cSKalle Valo struct brcmf_fws_hanger {
43705491d2cSKalle Valo 	u32 pushed;
43805491d2cSKalle Valo 	u32 popped;
43905491d2cSKalle Valo 	u32 failed_to_push;
44005491d2cSKalle Valo 	u32 failed_to_pop;
44105491d2cSKalle Valo 	u32 failed_slotfind;
44205491d2cSKalle Valo 	u32 slot_pos;
44305491d2cSKalle Valo 	struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS];
44405491d2cSKalle Valo };
44505491d2cSKalle Valo 
44605491d2cSKalle Valo struct brcmf_fws_macdesc_table {
44705491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE];
44805491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS];
44905491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor other;
45005491d2cSKalle Valo };
45105491d2cSKalle Valo 
45205491d2cSKalle Valo struct brcmf_fws_stats {
45305491d2cSKalle Valo 	u32 tlv_parse_failed;
45405491d2cSKalle Valo 	u32 tlv_invalid_type;
45505491d2cSKalle Valo 	u32 header_only_pkt;
45605491d2cSKalle Valo 	u32 header_pulls;
45705491d2cSKalle Valo 	u32 pkt2bus;
45805491d2cSKalle Valo 	u32 send_pkts[5];
45905491d2cSKalle Valo 	u32 requested_sent[5];
46005491d2cSKalle Valo 	u32 generic_error;
46105491d2cSKalle Valo 	u32 mac_update_failed;
46205491d2cSKalle Valo 	u32 mac_ps_update_failed;
46305491d2cSKalle Valo 	u32 if_update_failed;
46405491d2cSKalle Valo 	u32 packet_request_failed;
46505491d2cSKalle Valo 	u32 credit_request_failed;
46605491d2cSKalle Valo 	u32 rollback_success;
46705491d2cSKalle Valo 	u32 rollback_failed;
46805491d2cSKalle Valo 	u32 delayq_full_error;
46905491d2cSKalle Valo 	u32 supprq_full_error;
47005491d2cSKalle Valo 	u32 txs_indicate;
47105491d2cSKalle Valo 	u32 txs_discard;
47205491d2cSKalle Valo 	u32 txs_supp_core;
47305491d2cSKalle Valo 	u32 txs_supp_ps;
47405491d2cSKalle Valo 	u32 txs_tossed;
47505491d2cSKalle Valo 	u32 txs_host_tossed;
47605491d2cSKalle Valo 	u32 bus_flow_block;
47705491d2cSKalle Valo 	u32 fws_flow_block;
47805491d2cSKalle Valo };
47905491d2cSKalle Valo 
48005491d2cSKalle Valo struct brcmf_fws_info {
48105491d2cSKalle Valo 	struct brcmf_pub *drvr;
48205491d2cSKalle Valo 	spinlock_t spinlock;
48305491d2cSKalle Valo 	ulong flags;
48405491d2cSKalle Valo 	struct brcmf_fws_stats stats;
48505491d2cSKalle Valo 	struct brcmf_fws_hanger hanger;
48605491d2cSKalle Valo 	enum brcmf_fws_fcmode fcmode;
48705491d2cSKalle Valo 	bool fw_signals;
48805491d2cSKalle Valo 	bool bcmc_credit_check;
48905491d2cSKalle Valo 	struct brcmf_fws_macdesc_table desc;
49005491d2cSKalle Valo 	struct workqueue_struct *fws_wq;
49105491d2cSKalle Valo 	struct work_struct fws_dequeue_work;
49205491d2cSKalle Valo 	u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
49305491d2cSKalle Valo 	int fifo_credit[BRCMF_FWS_FIFO_COUNT];
494153e22c0SWright Feng 	int init_fifo_credit[BRCMF_FWS_FIFO_COUNT];
495683608bdSRaveendran Somu 	int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1]
496683608bdSRaveendran Somu 		[BRCMF_FWS_FIFO_AC_VO + 1];
49705491d2cSKalle Valo 	int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
49805491d2cSKalle Valo 	u32 fifo_credit_map;
49905491d2cSKalle Valo 	u32 fifo_delay_map;
50005491d2cSKalle Valo 	unsigned long borrow_defer_timestamp;
50105491d2cSKalle Valo 	bool bus_flow_blocked;
50205491d2cSKalle Valo 	bool creditmap_received;
50305491d2cSKalle Valo 	u8 mode;
50405491d2cSKalle Valo 	bool avoid_queueing;
50505491d2cSKalle Valo };
50605491d2cSKalle Valo 
50705491d2cSKalle Valo #define BRCMF_FWS_TLV_DEF(name, id, len) \
50805491d2cSKalle Valo 	case BRCMF_FWS_TYPE_ ## name: \
50905491d2cSKalle Valo 		return len;
51005491d2cSKalle Valo 
51105491d2cSKalle Valo /**
51205491d2cSKalle Valo  * brcmf_fws_get_tlv_len() - returns defined length for given tlv id.
51305491d2cSKalle Valo  *
51405491d2cSKalle Valo  * @fws: firmware-signalling information.
51505491d2cSKalle Valo  * @id: identifier of the TLV.
51605491d2cSKalle Valo  *
51705491d2cSKalle Valo  * Return: the specified length for the given TLV; Otherwise -EINVAL.
51805491d2cSKalle Valo  */
brcmf_fws_get_tlv_len(struct brcmf_fws_info * fws,enum brcmf_fws_tlv_type id)51905491d2cSKalle Valo static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
52005491d2cSKalle Valo 				 enum brcmf_fws_tlv_type id)
52105491d2cSKalle Valo {
52205491d2cSKalle Valo 	switch (id) {
52305491d2cSKalle Valo 	BRCMF_FWS_TLV_DEFLIST
52405491d2cSKalle Valo 	default:
52505491d2cSKalle Valo 		fws->stats.tlv_invalid_type++;
52605491d2cSKalle Valo 		break;
52705491d2cSKalle Valo 	}
52805491d2cSKalle Valo 	return -EINVAL;
52905491d2cSKalle Valo }
53005491d2cSKalle Valo #undef BRCMF_FWS_TLV_DEF
53105491d2cSKalle Valo 
brcmf_fws_lock(struct brcmf_fws_info * fws)53205491d2cSKalle Valo static void brcmf_fws_lock(struct brcmf_fws_info *fws)
53305491d2cSKalle Valo 		__acquires(&fws->spinlock)
53405491d2cSKalle Valo {
53505491d2cSKalle Valo 	spin_lock_irqsave(&fws->spinlock, fws->flags);
53605491d2cSKalle Valo }
53705491d2cSKalle Valo 
brcmf_fws_unlock(struct brcmf_fws_info * fws)53805491d2cSKalle Valo static void brcmf_fws_unlock(struct brcmf_fws_info *fws)
53905491d2cSKalle Valo 		__releases(&fws->spinlock)
54005491d2cSKalle Valo {
54105491d2cSKalle Valo 	spin_unlock_irqrestore(&fws->spinlock, fws->flags);
54205491d2cSKalle Valo }
54305491d2cSKalle Valo 
brcmf_fws_ifidx_match(struct sk_buff * skb,void * arg)54405491d2cSKalle Valo static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
54505491d2cSKalle Valo {
54605491d2cSKalle Valo 	u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
54705491d2cSKalle Valo 	return ifidx == *(int *)arg;
54805491d2cSKalle Valo }
54905491d2cSKalle Valo 
brcmf_fws_hanger_init(struct brcmf_fws_hanger * hanger)55005491d2cSKalle Valo static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)
55105491d2cSKalle Valo {
55205491d2cSKalle Valo 	int i;
55305491d2cSKalle Valo 
55405491d2cSKalle Valo 	memset(hanger, 0, sizeof(*hanger));
55505491d2cSKalle Valo 	for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
55605491d2cSKalle Valo 		hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
55705491d2cSKalle Valo }
55805491d2cSKalle Valo 
brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger * h)55905491d2cSKalle Valo static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
56005491d2cSKalle Valo {
56105491d2cSKalle Valo 	u32 i;
56205491d2cSKalle Valo 
56305491d2cSKalle Valo 	i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
56405491d2cSKalle Valo 
56505491d2cSKalle Valo 	while (i != h->slot_pos) {
56605491d2cSKalle Valo 		if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
56705491d2cSKalle Valo 			h->slot_pos = i;
56805491d2cSKalle Valo 			goto done;
56905491d2cSKalle Valo 		}
57005491d2cSKalle Valo 		i++;
57105491d2cSKalle Valo 		if (i == BRCMF_FWS_HANGER_MAXITEMS)
57205491d2cSKalle Valo 			i = 0;
57305491d2cSKalle Valo 	}
57405491d2cSKalle Valo 	brcmf_err("all slots occupied\n");
57505491d2cSKalle Valo 	h->failed_slotfind++;
57605491d2cSKalle Valo 	i = BRCMF_FWS_HANGER_MAXITEMS;
57705491d2cSKalle Valo done:
57805491d2cSKalle Valo 	return i;
57905491d2cSKalle Valo }
58005491d2cSKalle Valo 
brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger * h,struct sk_buff * pkt,u32 slot_id)58105491d2cSKalle Valo static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
58205491d2cSKalle Valo 				    struct sk_buff *pkt, u32 slot_id)
58305491d2cSKalle Valo {
58405491d2cSKalle Valo 	if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
58505491d2cSKalle Valo 		return -ENOENT;
58605491d2cSKalle Valo 
58705491d2cSKalle Valo 	if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
58805491d2cSKalle Valo 		brcmf_err("slot is not free\n");
58905491d2cSKalle Valo 		h->failed_to_push++;
59005491d2cSKalle Valo 		return -EINVAL;
59105491d2cSKalle Valo 	}
59205491d2cSKalle Valo 
59305491d2cSKalle Valo 	h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE;
59405491d2cSKalle Valo 	h->items[slot_id].pkt = pkt;
59505491d2cSKalle Valo 	h->pushed++;
59605491d2cSKalle Valo 	return 0;
59705491d2cSKalle Valo }
59805491d2cSKalle Valo 
brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger * h,u32 slot_id,struct sk_buff ** pktout,bool remove_item)59905491d2cSKalle Valo static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
60005491d2cSKalle Valo 					  u32 slot_id, struct sk_buff **pktout,
60105491d2cSKalle Valo 					  bool remove_item)
60205491d2cSKalle Valo {
60305491d2cSKalle Valo 	if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
60405491d2cSKalle Valo 		return -ENOENT;
60505491d2cSKalle Valo 
60605491d2cSKalle Valo 	if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
60705491d2cSKalle Valo 		brcmf_err("entry not in use\n");
60805491d2cSKalle Valo 		h->failed_to_pop++;
60905491d2cSKalle Valo 		return -EINVAL;
61005491d2cSKalle Valo 	}
61105491d2cSKalle Valo 
61205491d2cSKalle Valo 	*pktout = h->items[slot_id].pkt;
61305491d2cSKalle Valo 	if (remove_item) {
61405491d2cSKalle Valo 		h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
61505491d2cSKalle Valo 		h->items[slot_id].pkt = NULL;
61605491d2cSKalle Valo 		h->popped++;
61705491d2cSKalle Valo 	}
61805491d2cSKalle Valo 	return 0;
61905491d2cSKalle Valo }
62005491d2cSKalle Valo 
brcmf_fws_psq_flush(struct brcmf_fws_info * fws,struct pktq * q,int ifidx)621c80d26e8SPiotr Figiel static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q,
622c80d26e8SPiotr Figiel 				int ifidx)
623c80d26e8SPiotr Figiel {
624fcdd7a87SWright Feng 	struct brcmf_fws_hanger_item *hi;
625c80d26e8SPiotr Figiel 	bool (*matchfn)(struct sk_buff *, void *) = NULL;
626c80d26e8SPiotr Figiel 	struct sk_buff *skb;
627c80d26e8SPiotr Figiel 	int prec;
628c80d26e8SPiotr Figiel 	u32 hslot;
629c80d26e8SPiotr Figiel 
630c80d26e8SPiotr Figiel 	if (ifidx != -1)
631c80d26e8SPiotr Figiel 		matchfn = brcmf_fws_ifidx_match;
632c80d26e8SPiotr Figiel 	for (prec = 0; prec < q->num_prec; prec++) {
633c80d26e8SPiotr Figiel 		skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
634c80d26e8SPiotr Figiel 		while (skb) {
635c80d26e8SPiotr Figiel 			hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
636fcdd7a87SWright Feng 			hi = &fws->hanger.items[hslot];
637fcdd7a87SWright Feng 			WARN_ON(skb != hi->pkt);
638fcdd7a87SWright Feng 			hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
639c80d26e8SPiotr Figiel 			brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
640c80d26e8SPiotr Figiel 						true);
641c80d26e8SPiotr Figiel 			brcmu_pkt_buf_free_skb(skb);
642c80d26e8SPiotr Figiel 			skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
643c80d26e8SPiotr Figiel 		}
644c80d26e8SPiotr Figiel 	}
645c80d26e8SPiotr Figiel }
646c80d26e8SPiotr Figiel 
brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger * h,u32 slot_id)64705491d2cSKalle Valo static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
64805491d2cSKalle Valo 					    u32 slot_id)
64905491d2cSKalle Valo {
65005491d2cSKalle Valo 	if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
65105491d2cSKalle Valo 		return -ENOENT;
65205491d2cSKalle Valo 
65305491d2cSKalle Valo 	if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
65405491d2cSKalle Valo 		brcmf_err("entry not in use\n");
65505491d2cSKalle Valo 		return -EINVAL;
65605491d2cSKalle Valo 	}
65705491d2cSKalle Valo 
65805491d2cSKalle Valo 	h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
65905491d2cSKalle Valo 	return 0;
66005491d2cSKalle Valo }
66105491d2cSKalle Valo 
brcmf_fws_hanger_cleanup(struct brcmf_fws_info * fws,bool (* fn)(struct sk_buff *,void *),int ifidx)66205491d2cSKalle Valo static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
66305491d2cSKalle Valo 				     bool (*fn)(struct sk_buff *, void *),
66405491d2cSKalle Valo 				     int ifidx)
66505491d2cSKalle Valo {
66605491d2cSKalle Valo 	struct brcmf_fws_hanger *h = &fws->hanger;
66705491d2cSKalle Valo 	struct sk_buff *skb;
66805491d2cSKalle Valo 	int i;
66905491d2cSKalle Valo 	enum brcmf_fws_hanger_item_state s;
67005491d2cSKalle Valo 
67105491d2cSKalle Valo 	for (i = 0; i < ARRAY_SIZE(h->items); i++) {
67205491d2cSKalle Valo 		s = h->items[i].state;
67305491d2cSKalle Valo 		if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
67405491d2cSKalle Valo 		    s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
67505491d2cSKalle Valo 			skb = h->items[i].pkt;
67605491d2cSKalle Valo 			if (fn == NULL || fn(skb, &ifidx)) {
67705491d2cSKalle Valo 				/* suppress packets freed from psq */
67805491d2cSKalle Valo 				if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE)
67905491d2cSKalle Valo 					brcmu_pkt_buf_free_skb(skb);
68005491d2cSKalle Valo 				h->items[i].state =
68105491d2cSKalle Valo 					BRCMF_FWS_HANGER_ITEM_STATE_FREE;
68205491d2cSKalle Valo 			}
68305491d2cSKalle Valo 		}
68405491d2cSKalle Valo 	}
68505491d2cSKalle Valo }
68605491d2cSKalle Valo 
brcmf_fws_macdesc_set_name(struct brcmf_fws_info * fws,struct brcmf_fws_mac_descriptor * desc)68705491d2cSKalle Valo static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws,
68805491d2cSKalle Valo 				       struct brcmf_fws_mac_descriptor *desc)
68905491d2cSKalle Valo {
69005491d2cSKalle Valo 	if (desc == &fws->desc.other)
691*bf99f11dSWolfram Sang 		strscpy(desc->name, "MAC-OTHER", sizeof(desc->name));
69205491d2cSKalle Valo 	else if (desc->mac_handle)
69305491d2cSKalle Valo 		scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d",
69405491d2cSKalle Valo 			  desc->mac_handle, desc->interface_id);
69505491d2cSKalle Valo 	else
69605491d2cSKalle Valo 		scnprintf(desc->name, sizeof(desc->name), "MACIF:%d",
69705491d2cSKalle Valo 			  desc->interface_id);
69805491d2cSKalle Valo }
69905491d2cSKalle Valo 
brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor * desc,u8 * addr,u8 ifidx)70005491d2cSKalle Valo static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc,
70105491d2cSKalle Valo 				   u8 *addr, u8 ifidx)
70205491d2cSKalle Valo {
70305491d2cSKalle Valo 	brcmf_dbg(TRACE,
70405491d2cSKalle Valo 		  "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx);
70505491d2cSKalle Valo 	desc->occupied = 1;
70605491d2cSKalle Valo 	desc->state = BRCMF_FWS_STATE_OPEN;
70705491d2cSKalle Valo 	desc->requested_credit = 0;
70805491d2cSKalle Valo 	desc->requested_packet = 0;
70937a869ecSHante Meuleman 	/* depending on use may need ifp->bsscfgidx instead */
71005491d2cSKalle Valo 	desc->interface_id = ifidx;
71105491d2cSKalle Valo 	desc->ac_bitmap = 0xff; /* update this when handling APSD */
71205491d2cSKalle Valo 	if (addr)
71305491d2cSKalle Valo 		memcpy(&desc->ea[0], addr, ETH_ALEN);
71405491d2cSKalle Valo }
71505491d2cSKalle Valo 
71605491d2cSKalle Valo static
brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor * desc)71705491d2cSKalle Valo void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc)
71805491d2cSKalle Valo {
71905491d2cSKalle Valo 	brcmf_dbg(TRACE,
72005491d2cSKalle Valo 		  "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id);
72105491d2cSKalle Valo 	desc->occupied = 0;
72205491d2cSKalle Valo 	desc->state = BRCMF_FWS_STATE_CLOSE;
72305491d2cSKalle Valo 	desc->requested_credit = 0;
72405491d2cSKalle Valo 	desc->requested_packet = 0;
72505491d2cSKalle Valo }
72605491d2cSKalle Valo 
72705491d2cSKalle Valo static struct brcmf_fws_mac_descriptor *
brcmf_fws_macdesc_lookup(struct brcmf_fws_info * fws,u8 * ea)72805491d2cSKalle Valo brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)
72905491d2cSKalle Valo {
73005491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry;
73105491d2cSKalle Valo 	int i;
73205491d2cSKalle Valo 
73305491d2cSKalle Valo 	if (ea == NULL)
73405491d2cSKalle Valo 		return ERR_PTR(-EINVAL);
73505491d2cSKalle Valo 
73605491d2cSKalle Valo 	entry = &fws->desc.nodes[0];
73705491d2cSKalle Valo 	for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) {
73805491d2cSKalle Valo 		if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN))
73905491d2cSKalle Valo 			return entry;
74005491d2cSKalle Valo 		entry++;
74105491d2cSKalle Valo 	}
74205491d2cSKalle Valo 
74305491d2cSKalle Valo 	return ERR_PTR(-ENOENT);
74405491d2cSKalle Valo }
74505491d2cSKalle Valo 
74605491d2cSKalle Valo static struct brcmf_fws_mac_descriptor*
brcmf_fws_macdesc_find(struct brcmf_fws_info * fws,struct brcmf_if * ifp,u8 * da)74705491d2cSKalle Valo brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da)
74805491d2cSKalle Valo {
749871a825cSColin Ian King 	struct brcmf_fws_mac_descriptor *entry;
75005491d2cSKalle Valo 	bool multicast;
75105491d2cSKalle Valo 
75205491d2cSKalle Valo 	multicast = is_multicast_ether_addr(da);
75305491d2cSKalle Valo 
75405491d2cSKalle Valo 	/* Multicast destination, STA and P2P clients get the interface entry.
75505491d2cSKalle Valo 	 * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
75605491d2cSKalle Valo 	 * have their own entry.
75705491d2cSKalle Valo 	 */
75805491d2cSKalle Valo 	if (multicast && ifp->fws_desc) {
75905491d2cSKalle Valo 		entry = ifp->fws_desc;
76005491d2cSKalle Valo 		goto done;
76105491d2cSKalle Valo 	}
76205491d2cSKalle Valo 
76305491d2cSKalle Valo 	entry = brcmf_fws_macdesc_lookup(fws, da);
76405491d2cSKalle Valo 	if (IS_ERR(entry))
76505491d2cSKalle Valo 		entry = ifp->fws_desc;
76605491d2cSKalle Valo 
76705491d2cSKalle Valo done:
76805491d2cSKalle Valo 	return entry;
76905491d2cSKalle Valo }
77005491d2cSKalle Valo 
brcmf_fws_macdesc_closed(struct brcmf_fws_info * fws,struct brcmf_fws_mac_descriptor * entry,int fifo)77105491d2cSKalle Valo static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws,
77205491d2cSKalle Valo 				     struct brcmf_fws_mac_descriptor *entry,
77305491d2cSKalle Valo 				     int fifo)
77405491d2cSKalle Valo {
77505491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *if_entry;
77605491d2cSKalle Valo 	bool closed;
77705491d2cSKalle Valo 
77805491d2cSKalle Valo 	/* for unique destination entries the related interface
77905491d2cSKalle Valo 	 * may be closed.
78005491d2cSKalle Valo 	 */
78105491d2cSKalle Valo 	if (entry->mac_handle) {
78205491d2cSKalle Valo 		if_entry = &fws->desc.iface[entry->interface_id];
78305491d2cSKalle Valo 		if (if_entry->state == BRCMF_FWS_STATE_CLOSE)
78405491d2cSKalle Valo 			return true;
78505491d2cSKalle Valo 	}
78605491d2cSKalle Valo 	/* an entry is closed when the state is closed and
78705491d2cSKalle Valo 	 * the firmware did not request anything.
78805491d2cSKalle Valo 	 */
78905491d2cSKalle Valo 	closed = entry->state == BRCMF_FWS_STATE_CLOSE &&
79005491d2cSKalle Valo 		 !entry->requested_credit && !entry->requested_packet;
79105491d2cSKalle Valo 
79205491d2cSKalle Valo 	/* Or firmware does not allow traffic for given fifo */
79305491d2cSKalle Valo 	return closed || !(entry->ac_bitmap & BIT(fifo));
79405491d2cSKalle Valo }
79505491d2cSKalle Valo 
brcmf_fws_macdesc_cleanup(struct brcmf_fws_info * fws,struct brcmf_fws_mac_descriptor * entry,int ifidx)79605491d2cSKalle Valo static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,
79705491d2cSKalle Valo 				      struct brcmf_fws_mac_descriptor *entry,
79805491d2cSKalle Valo 				      int ifidx)
79905491d2cSKalle Valo {
80005491d2cSKalle Valo 	if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
80105491d2cSKalle Valo 		brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
80205491d2cSKalle Valo 		entry->occupied = !!(entry->psq.len);
80305491d2cSKalle Valo 	}
80405491d2cSKalle Valo }
80505491d2cSKalle Valo 
brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info * fws,bool (* fn)(struct sk_buff *,void *),int ifidx)80605491d2cSKalle Valo static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,
80705491d2cSKalle Valo 				      bool (*fn)(struct sk_buff *, void *),
80805491d2cSKalle Valo 				      int ifidx)
80905491d2cSKalle Valo {
81005491d2cSKalle Valo 	struct brcmf_fws_hanger_item *hi;
81105491d2cSKalle Valo 	struct pktq *txq;
81205491d2cSKalle Valo 	struct sk_buff *skb;
81305491d2cSKalle Valo 	int prec;
81405491d2cSKalle Valo 	u32 hslot;
81505491d2cSKalle Valo 
81605491d2cSKalle Valo 	txq = brcmf_bus_gettxq(fws->drvr->bus_if);
81705491d2cSKalle Valo 	if (IS_ERR(txq)) {
81805491d2cSKalle Valo 		brcmf_dbg(TRACE, "no txq to clean up\n");
81905491d2cSKalle Valo 		return;
82005491d2cSKalle Valo 	}
82105491d2cSKalle Valo 
82205491d2cSKalle Valo 	for (prec = 0; prec < txq->num_prec; prec++) {
82305491d2cSKalle Valo 		skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
82405491d2cSKalle Valo 		while (skb) {
82505491d2cSKalle Valo 			hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
82605491d2cSKalle Valo 			hi = &fws->hanger.items[hslot];
82705491d2cSKalle Valo 			WARN_ON(skb != hi->pkt);
82805491d2cSKalle Valo 			hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
82905491d2cSKalle Valo 			brcmu_pkt_buf_free_skb(skb);
83005491d2cSKalle Valo 			skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
83105491d2cSKalle Valo 		}
83205491d2cSKalle Valo 	}
83305491d2cSKalle Valo }
83405491d2cSKalle Valo 
brcmf_fws_cleanup(struct brcmf_fws_info * fws,int ifidx)83505491d2cSKalle Valo static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
83605491d2cSKalle Valo {
83705491d2cSKalle Valo 	int i;
83805491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *table;
83905491d2cSKalle Valo 	bool (*matchfn)(struct sk_buff *, void *) = NULL;
84005491d2cSKalle Valo 
84105491d2cSKalle Valo 	if (fws == NULL)
84205491d2cSKalle Valo 		return;
84305491d2cSKalle Valo 
84405491d2cSKalle Valo 	if (ifidx != -1)
84505491d2cSKalle Valo 		matchfn = brcmf_fws_ifidx_match;
84605491d2cSKalle Valo 
84705491d2cSKalle Valo 	/* cleanup individual nodes */
84805491d2cSKalle Valo 	table = &fws->desc.nodes[0];
84905491d2cSKalle Valo 	for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)
85005491d2cSKalle Valo 		brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx);
85105491d2cSKalle Valo 
85205491d2cSKalle Valo 	brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx);
85305491d2cSKalle Valo 	brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx);
85405491d2cSKalle Valo 	brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
85505491d2cSKalle Valo }
85605491d2cSKalle Valo 
brcmf_fws_hdrpush(struct brcmf_fws_info * fws,struct sk_buff * skb)85705491d2cSKalle Valo static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
85805491d2cSKalle Valo {
85905491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
86005491d2cSKalle Valo 	u8 *wlh;
86105491d2cSKalle Valo 	u16 data_offset = 0;
86205491d2cSKalle Valo 	u8 fillers;
86305491d2cSKalle Valo 	__le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
86405491d2cSKalle Valo 	__le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq);
86505491d2cSKalle Valo 
86605491d2cSKalle Valo 	brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n",
86705491d2cSKalle Valo 		  entry->name, brcmf_skb_if_flags_get_field(skb, INDEX),
86805491d2cSKalle Valo 		  (le32_to_cpu(pkttag) >> 8) & 0xffff,
86905491d2cSKalle Valo 		  brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq);
87005491d2cSKalle Valo 	if (entry->send_tim_signal)
87105491d2cSKalle Valo 		data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
87205491d2cSKalle Valo 	if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
87305491d2cSKalle Valo 		data_offset += BRCMF_FWS_TYPE_SEQ_LEN;
87405491d2cSKalle Valo 	/* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
87505491d2cSKalle Valo 	data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
87605491d2cSKalle Valo 	fillers = round_up(data_offset, 4) - data_offset;
87705491d2cSKalle Valo 	data_offset += fillers;
87805491d2cSKalle Valo 
87905491d2cSKalle Valo 	skb_push(skb, data_offset);
88005491d2cSKalle Valo 	wlh = skb->data;
88105491d2cSKalle Valo 
88205491d2cSKalle Valo 	wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
88305491d2cSKalle Valo 	wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
88405491d2cSKalle Valo 	memcpy(&wlh[2], &pkttag, sizeof(pkttag));
88505491d2cSKalle Valo 	if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
88605491d2cSKalle Valo 		wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN;
88705491d2cSKalle Valo 		memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq,
88805491d2cSKalle Valo 		       sizeof(pktseq));
88905491d2cSKalle Valo 	}
89005491d2cSKalle Valo 	wlh += wlh[1] + 2;
89105491d2cSKalle Valo 
89205491d2cSKalle Valo 	if (entry->send_tim_signal) {
893b92c017dSzhengbin 		entry->send_tim_signal = false;
89405491d2cSKalle Valo 		wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
89505491d2cSKalle Valo 		wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
89605491d2cSKalle Valo 		wlh[2] = entry->mac_handle;
89705491d2cSKalle Valo 		wlh[3] = entry->traffic_pending_bmp;
89805491d2cSKalle Valo 		brcmf_dbg(TRACE, "adding TIM info: handle %d bmp 0x%X\n",
89905491d2cSKalle Valo 			  entry->mac_handle, entry->traffic_pending_bmp);
90005491d2cSKalle Valo 		wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
90105491d2cSKalle Valo 		entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
90205491d2cSKalle Valo 	}
90305491d2cSKalle Valo 	if (fillers)
90405491d2cSKalle Valo 		memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
90505491d2cSKalle Valo 
90605491d2cSKalle Valo 	return (u8)(data_offset >> 2);
90705491d2cSKalle Valo }
90805491d2cSKalle Valo 
brcmf_fws_tim_update(struct brcmf_fws_info * fws,struct brcmf_fws_mac_descriptor * entry,int fifo,bool send_immediately)90905491d2cSKalle Valo static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
91005491d2cSKalle Valo 				 struct brcmf_fws_mac_descriptor *entry,
91105491d2cSKalle Valo 				 int fifo, bool send_immediately)
91205491d2cSKalle Valo {
91305491d2cSKalle Valo 	struct sk_buff *skb;
91405491d2cSKalle Valo 	struct brcmf_skbuff_cb *skcb;
91505491d2cSKalle Valo 	s32 err;
91605491d2cSKalle Valo 	u32 len;
91705491d2cSKalle Valo 	u8 data_offset;
91805491d2cSKalle Valo 	int ifidx;
91905491d2cSKalle Valo 
92005491d2cSKalle Valo 	/* check delayedQ and suppressQ in one call using bitmap */
92105491d2cSKalle Valo 	if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
92205491d2cSKalle Valo 		entry->traffic_pending_bmp &= ~NBITVAL(fifo);
92305491d2cSKalle Valo 	else
92405491d2cSKalle Valo 		entry->traffic_pending_bmp |= NBITVAL(fifo);
92505491d2cSKalle Valo 
92605491d2cSKalle Valo 	entry->send_tim_signal = false;
92705491d2cSKalle Valo 	if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)
92805491d2cSKalle Valo 		entry->send_tim_signal = true;
92905491d2cSKalle Valo 	if (send_immediately && entry->send_tim_signal &&
93005491d2cSKalle Valo 	    entry->state == BRCMF_FWS_STATE_CLOSE) {
93105491d2cSKalle Valo 		/* create a dummy packet and sent that. The traffic          */
93205491d2cSKalle Valo 		/* bitmap info will automatically be attached to that packet */
93305491d2cSKalle Valo 		len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
93405491d2cSKalle Valo 		      BRCMF_FWS_TYPE_SEQ_LEN +
93505491d2cSKalle Valo 		      BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
93605491d2cSKalle Valo 		      4 + fws->drvr->hdrlen;
93705491d2cSKalle Valo 		skb = brcmu_pkt_buf_get_skb(len);
93805491d2cSKalle Valo 		if (skb == NULL)
93905491d2cSKalle Valo 			return false;
94005491d2cSKalle Valo 		skb_pull(skb, len);
94105491d2cSKalle Valo 		skcb = brcmf_skbcb(skb);
94205491d2cSKalle Valo 		skcb->mac = entry;
94305491d2cSKalle Valo 		skcb->state = BRCMF_FWS_SKBSTATE_TIM;
94405491d2cSKalle Valo 		skcb->htod = 0;
94505491d2cSKalle Valo 		skcb->htod_seq = 0;
94605491d2cSKalle Valo 		data_offset = brcmf_fws_hdrpush(fws, skb);
94705491d2cSKalle Valo 		ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
94805491d2cSKalle Valo 		brcmf_fws_unlock(fws);
94905491d2cSKalle Valo 		err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
95005491d2cSKalle Valo 		brcmf_fws_lock(fws);
95105491d2cSKalle Valo 		if (err)
95205491d2cSKalle Valo 			brcmu_pkt_buf_free_skb(skb);
95305491d2cSKalle Valo 		return true;
95405491d2cSKalle Valo 	}
95505491d2cSKalle Valo 	return false;
95605491d2cSKalle Valo }
95705491d2cSKalle Valo 
95805491d2cSKalle Valo static void
brcmf_fws_flow_control_check(struct brcmf_fws_info * fws,struct pktq * pq,u8 if_id)95905491d2cSKalle Valo brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
96005491d2cSKalle Valo 			     u8 if_id)
96105491d2cSKalle Valo {
96205491d2cSKalle Valo 	struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
96305491d2cSKalle Valo 
96405491d2cSKalle Valo 	if (WARN_ON(!ifp))
96505491d2cSKalle Valo 		return;
96605491d2cSKalle Valo 
96705491d2cSKalle Valo 	if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
96805491d2cSKalle Valo 	    pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER)
96905491d2cSKalle Valo 		brcmf_txflowblock_if(ifp,
97005491d2cSKalle Valo 				     BRCMF_NETIF_STOP_REASON_FWS_FC, false);
97105491d2cSKalle Valo 	if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
97205491d2cSKalle Valo 	    pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) {
97305491d2cSKalle Valo 		fws->stats.fws_flow_block++;
97405491d2cSKalle Valo 		brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);
97505491d2cSKalle Valo 	}
97605491d2cSKalle Valo 	return;
97705491d2cSKalle Valo }
97805491d2cSKalle Valo 
brcmf_fws_rssi_indicate(struct brcmf_fws_info * fws,s8 rssi)97905491d2cSKalle Valo static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
98005491d2cSKalle Valo {
98105491d2cSKalle Valo 	brcmf_dbg(CTL, "rssi %d\n", rssi);
98205491d2cSKalle Valo 	return 0;
98305491d2cSKalle Valo }
98405491d2cSKalle Valo 
98505491d2cSKalle Valo static
brcmf_fws_macdesc_indicate(struct brcmf_fws_info * fws,u8 type,u8 * data)98605491d2cSKalle Valo int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
98705491d2cSKalle Valo {
98805491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry, *existing;
98905491d2cSKalle Valo 	u8 mac_handle;
99005491d2cSKalle Valo 	u8 ifidx;
99105491d2cSKalle Valo 	u8 *addr;
99205491d2cSKalle Valo 
99305491d2cSKalle Valo 	mac_handle = *data++;
99405491d2cSKalle Valo 	ifidx = *data++;
99505491d2cSKalle Valo 	addr = data;
99605491d2cSKalle Valo 
99705491d2cSKalle Valo 	entry = &fws->desc.nodes[mac_handle & 0x1F];
99805491d2cSKalle Valo 	if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
99905491d2cSKalle Valo 		if (entry->occupied) {
100005491d2cSKalle Valo 			brcmf_dbg(TRACE, "deleting %s mac %pM\n",
100105491d2cSKalle Valo 				  entry->name, addr);
100205491d2cSKalle Valo 			brcmf_fws_lock(fws);
100305491d2cSKalle Valo 			brcmf_fws_macdesc_cleanup(fws, entry, -1);
100405491d2cSKalle Valo 			brcmf_fws_macdesc_deinit(entry);
100505491d2cSKalle Valo 			brcmf_fws_unlock(fws);
100605491d2cSKalle Valo 		} else
100705491d2cSKalle Valo 			fws->stats.mac_update_failed++;
100805491d2cSKalle Valo 		return 0;
100905491d2cSKalle Valo 	}
101005491d2cSKalle Valo 
101105491d2cSKalle Valo 	existing = brcmf_fws_macdesc_lookup(fws, addr);
101205491d2cSKalle Valo 	if (IS_ERR(existing)) {
101305491d2cSKalle Valo 		if (!entry->occupied) {
101405491d2cSKalle Valo 			brcmf_fws_lock(fws);
101505491d2cSKalle Valo 			entry->mac_handle = mac_handle;
101605491d2cSKalle Valo 			brcmf_fws_macdesc_init(entry, addr, ifidx);
101705491d2cSKalle Valo 			brcmf_fws_macdesc_set_name(fws, entry);
101805491d2cSKalle Valo 			brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
101905491d2cSKalle Valo 					BRCMF_FWS_PSQ_LEN);
102005491d2cSKalle Valo 			brcmf_fws_unlock(fws);
102105491d2cSKalle Valo 			brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
102205491d2cSKalle Valo 		} else {
102305491d2cSKalle Valo 			fws->stats.mac_update_failed++;
102405491d2cSKalle Valo 		}
102505491d2cSKalle Valo 	} else {
102605491d2cSKalle Valo 		if (entry != existing) {
102705491d2cSKalle Valo 			brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
102805491d2cSKalle Valo 			brcmf_fws_lock(fws);
102905491d2cSKalle Valo 			memcpy(entry, existing,
103005491d2cSKalle Valo 			       offsetof(struct brcmf_fws_mac_descriptor, psq));
103105491d2cSKalle Valo 			entry->mac_handle = mac_handle;
103205491d2cSKalle Valo 			brcmf_fws_macdesc_deinit(existing);
103305491d2cSKalle Valo 			brcmf_fws_macdesc_set_name(fws, entry);
103405491d2cSKalle Valo 			brcmf_fws_unlock(fws);
103505491d2cSKalle Valo 			brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
103605491d2cSKalle Valo 				  addr);
103705491d2cSKalle Valo 		} else {
103805491d2cSKalle Valo 			brcmf_dbg(TRACE, "use existing\n");
103905491d2cSKalle Valo 			WARN_ON(entry->mac_handle != mac_handle);
104005491d2cSKalle Valo 			/* TODO: what should we do here: continue, reinit, .. */
104105491d2cSKalle Valo 		}
104205491d2cSKalle Valo 	}
104305491d2cSKalle Valo 	return 0;
104405491d2cSKalle Valo }
104505491d2cSKalle Valo 
brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info * fws,u8 type,u8 * data)104605491d2cSKalle Valo static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
104705491d2cSKalle Valo 					    u8 type, u8 *data)
104805491d2cSKalle Valo {
104905491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry;
105005491d2cSKalle Valo 	u8 mac_handle;
105105491d2cSKalle Valo 	int ret;
105205491d2cSKalle Valo 
105305491d2cSKalle Valo 	mac_handle = data[0];
105405491d2cSKalle Valo 	entry = &fws->desc.nodes[mac_handle & 0x1F];
105505491d2cSKalle Valo 	if (!entry->occupied) {
105605491d2cSKalle Valo 		fws->stats.mac_ps_update_failed++;
105705491d2cSKalle Valo 		return -ESRCH;
105805491d2cSKalle Valo 	}
105905491d2cSKalle Valo 	brcmf_fws_lock(fws);
106005491d2cSKalle Valo 	/* a state update should wipe old credits */
106105491d2cSKalle Valo 	entry->requested_credit = 0;
106205491d2cSKalle Valo 	entry->requested_packet = 0;
106305491d2cSKalle Valo 	if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
106405491d2cSKalle Valo 		entry->state = BRCMF_FWS_STATE_OPEN;
106505491d2cSKalle Valo 		ret = BRCMF_FWS_RET_OK_SCHEDULE;
106605491d2cSKalle Valo 	} else {
106705491d2cSKalle Valo 		entry->state = BRCMF_FWS_STATE_CLOSE;
106805491d2cSKalle Valo 		brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false);
106905491d2cSKalle Valo 		brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false);
107005491d2cSKalle Valo 		brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false);
107105491d2cSKalle Valo 		brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
107205491d2cSKalle Valo 		ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
107305491d2cSKalle Valo 	}
107405491d2cSKalle Valo 	brcmf_fws_unlock(fws);
107505491d2cSKalle Valo 	return ret;
107605491d2cSKalle Valo }
107705491d2cSKalle Valo 
brcmf_fws_interface_state_indicate(struct brcmf_fws_info * fws,u8 type,u8 * data)107805491d2cSKalle Valo static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
107905491d2cSKalle Valo 					      u8 type, u8 *data)
108005491d2cSKalle Valo {
108105491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry;
108205491d2cSKalle Valo 	u8 ifidx;
108305491d2cSKalle Valo 	int ret;
108405491d2cSKalle Valo 
108505491d2cSKalle Valo 	ifidx = data[0];
108605491d2cSKalle Valo 
108705491d2cSKalle Valo 	if (ifidx >= BRCMF_MAX_IFS) {
108805491d2cSKalle Valo 		ret = -ERANGE;
108905491d2cSKalle Valo 		goto fail;
109005491d2cSKalle Valo 	}
109105491d2cSKalle Valo 
109205491d2cSKalle Valo 	entry = &fws->desc.iface[ifidx];
109305491d2cSKalle Valo 	if (!entry->occupied) {
109405491d2cSKalle Valo 		ret = -ESRCH;
109505491d2cSKalle Valo 		goto fail;
109605491d2cSKalle Valo 	}
109705491d2cSKalle Valo 
109805491d2cSKalle Valo 	brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
109905491d2cSKalle Valo 		  entry->name);
110005491d2cSKalle Valo 	brcmf_fws_lock(fws);
110105491d2cSKalle Valo 	switch (type) {
110205491d2cSKalle Valo 	case BRCMF_FWS_TYPE_INTERFACE_OPEN:
110305491d2cSKalle Valo 		entry->state = BRCMF_FWS_STATE_OPEN;
110405491d2cSKalle Valo 		ret = BRCMF_FWS_RET_OK_SCHEDULE;
110505491d2cSKalle Valo 		break;
110605491d2cSKalle Valo 	case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
110705491d2cSKalle Valo 		entry->state = BRCMF_FWS_STATE_CLOSE;
110805491d2cSKalle Valo 		ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
110905491d2cSKalle Valo 		break;
111005491d2cSKalle Valo 	default:
111105491d2cSKalle Valo 		ret = -EINVAL;
111205491d2cSKalle Valo 		brcmf_fws_unlock(fws);
111305491d2cSKalle Valo 		goto fail;
111405491d2cSKalle Valo 	}
111505491d2cSKalle Valo 	brcmf_fws_unlock(fws);
111605491d2cSKalle Valo 	return ret;
111705491d2cSKalle Valo 
111805491d2cSKalle Valo fail:
111905491d2cSKalle Valo 	fws->stats.if_update_failed++;
112005491d2cSKalle Valo 	return ret;
112105491d2cSKalle Valo }
112205491d2cSKalle Valo 
brcmf_fws_request_indicate(struct brcmf_fws_info * fws,u8 type,u8 * data)112305491d2cSKalle Valo static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
112405491d2cSKalle Valo 				      u8 *data)
112505491d2cSKalle Valo {
112605491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry;
112705491d2cSKalle Valo 
112805491d2cSKalle Valo 	entry = &fws->desc.nodes[data[1] & 0x1F];
112905491d2cSKalle Valo 	if (!entry->occupied) {
113005491d2cSKalle Valo 		if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
113105491d2cSKalle Valo 			fws->stats.credit_request_failed++;
113205491d2cSKalle Valo 		else
113305491d2cSKalle Valo 			fws->stats.packet_request_failed++;
113405491d2cSKalle Valo 		return -ESRCH;
113505491d2cSKalle Valo 	}
113605491d2cSKalle Valo 
113705491d2cSKalle Valo 	brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
113805491d2cSKalle Valo 		  brcmf_fws_get_tlv_name(type), type, entry->name,
113905491d2cSKalle Valo 		  data[0], data[2]);
114005491d2cSKalle Valo 	brcmf_fws_lock(fws);
114105491d2cSKalle Valo 	if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
114205491d2cSKalle Valo 		entry->requested_credit = data[0];
114305491d2cSKalle Valo 	else
114405491d2cSKalle Valo 		entry->requested_packet = data[0];
114505491d2cSKalle Valo 
114605491d2cSKalle Valo 	entry->ac_bitmap = data[2];
114705491d2cSKalle Valo 	brcmf_fws_unlock(fws);
114805491d2cSKalle Valo 	return BRCMF_FWS_RET_OK_SCHEDULE;
114905491d2cSKalle Valo }
115005491d2cSKalle Valo 
115105491d2cSKalle Valo static void
brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor * entry,struct sk_buff * skb)115205491d2cSKalle Valo brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry,
115305491d2cSKalle Valo 				 struct sk_buff *skb)
115405491d2cSKalle Valo {
115505491d2cSKalle Valo 	if (entry->requested_credit > 0) {
115605491d2cSKalle Valo 		entry->requested_credit--;
115705491d2cSKalle Valo 		brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
115805491d2cSKalle Valo 		brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1);
115905491d2cSKalle Valo 		if (entry->state != BRCMF_FWS_STATE_CLOSE)
116005491d2cSKalle Valo 			brcmf_err("requested credit set while mac not closed!\n");
116105491d2cSKalle Valo 	} else if (entry->requested_packet > 0) {
116205491d2cSKalle Valo 		entry->requested_packet--;
116305491d2cSKalle Valo 		brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
116405491d2cSKalle Valo 		brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
116505491d2cSKalle Valo 		if (entry->state != BRCMF_FWS_STATE_CLOSE)
116605491d2cSKalle Valo 			brcmf_err("requested packet set while mac not closed!\n");
116705491d2cSKalle Valo 	} else {
116805491d2cSKalle Valo 		brcmf_skb_if_flags_set_field(skb, REQUESTED, 0);
116905491d2cSKalle Valo 		brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
117005491d2cSKalle Valo 	}
117105491d2cSKalle Valo }
117205491d2cSKalle Valo 
brcmf_fws_macdesc_return_req_credit(struct sk_buff * skb)117305491d2cSKalle Valo static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb)
117405491d2cSKalle Valo {
117505491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
117605491d2cSKalle Valo 
117705491d2cSKalle Valo 	if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) &&
117805491d2cSKalle Valo 	    (entry->state == BRCMF_FWS_STATE_CLOSE))
117905491d2cSKalle Valo 		entry->requested_credit++;
118005491d2cSKalle Valo }
118105491d2cSKalle Valo 
brcmf_fws_return_credits(struct brcmf_fws_info * fws,u8 fifo,u8 credits)118205491d2cSKalle Valo static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
118305491d2cSKalle Valo 				     u8 fifo, u8 credits)
118405491d2cSKalle Valo {
118505491d2cSKalle Valo 	int lender_ac;
118605491d2cSKalle Valo 	int *borrowed;
118705491d2cSKalle Valo 	int *fifo_credit;
118805491d2cSKalle Valo 
118905491d2cSKalle Valo 	if (!credits)
119005491d2cSKalle Valo 		return;
119105491d2cSKalle Valo 
1192bbf7ae3dSDouble Lo 	fws->fifo_credit_map |= 1 << fifo;
1193bbf7ae3dSDouble Lo 
1194683608bdSRaveendran Somu 	if (fifo > BRCMF_FWS_FIFO_AC_BK &&
1195683608bdSRaveendran Somu 	    fifo <= BRCMF_FWS_FIFO_AC_VO) {
119605491d2cSKalle Valo 		for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
119705491d2cSKalle Valo 		     lender_ac--) {
1198683608bdSRaveendran Somu 			borrowed = &fws->credits_borrowed[fifo][lender_ac];
119905491d2cSKalle Valo 			if (*borrowed) {
120005491d2cSKalle Valo 				fws->fifo_credit_map |= (1 << lender_ac);
120105491d2cSKalle Valo 				fifo_credit = &fws->fifo_credit[lender_ac];
120205491d2cSKalle Valo 				if (*borrowed >= credits) {
120305491d2cSKalle Valo 					*borrowed -= credits;
120405491d2cSKalle Valo 					*fifo_credit += credits;
120505491d2cSKalle Valo 					return;
120605491d2cSKalle Valo 				} else {
120705491d2cSKalle Valo 					credits -= *borrowed;
120805491d2cSKalle Valo 					*fifo_credit += *borrowed;
120905491d2cSKalle Valo 					*borrowed = 0;
121005491d2cSKalle Valo 				}
121105491d2cSKalle Valo 			}
121205491d2cSKalle Valo 		}
121305491d2cSKalle Valo 	}
121405491d2cSKalle Valo 
1215683608bdSRaveendran Somu 	if (credits) {
121605491d2cSKalle Valo 		fws->fifo_credit[fifo] += credits;
1217683608bdSRaveendran Somu 	}
1218683608bdSRaveendran Somu 
1219153e22c0SWright Feng 	if (fws->fifo_credit[fifo] > fws->init_fifo_credit[fifo])
1220153e22c0SWright Feng 		fws->fifo_credit[fifo] = fws->init_fifo_credit[fifo];
1221153e22c0SWright Feng 
122205491d2cSKalle Valo }
122305491d2cSKalle Valo 
brcmf_fws_schedule_deq(struct brcmf_fws_info * fws)122405491d2cSKalle Valo static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
122505491d2cSKalle Valo {
122605491d2cSKalle Valo 	/* only schedule dequeue when there are credits for delayed traffic */
122705491d2cSKalle Valo 	if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
122805491d2cSKalle Valo 	    (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
122905491d2cSKalle Valo 		queue_work(fws->fws_wq, &fws->fws_dequeue_work);
123005491d2cSKalle Valo }
123105491d2cSKalle Valo 
brcmf_fws_enq(struct brcmf_fws_info * fws,enum brcmf_fws_skb_state state,int fifo,struct sk_buff * p)123205491d2cSKalle Valo static int brcmf_fws_enq(struct brcmf_fws_info *fws,
123305491d2cSKalle Valo 			 enum brcmf_fws_skb_state state, int fifo,
123405491d2cSKalle Valo 			 struct sk_buff *p)
123505491d2cSKalle Valo {
1236dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = fws->drvr;
123705491d2cSKalle Valo 	int prec = 2 * fifo;
123805491d2cSKalle Valo 	u32 *qfull_stat = &fws->stats.delayq_full_error;
123905491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry;
124005491d2cSKalle Valo 	struct pktq *pq;
124105491d2cSKalle Valo 	struct sk_buff_head *queue;
124205491d2cSKalle Valo 	struct sk_buff *p_head;
124305491d2cSKalle Valo 	struct sk_buff *p_tail;
124405491d2cSKalle Valo 	u32 fr_new;
124505491d2cSKalle Valo 	u32 fr_compare;
124605491d2cSKalle Valo 
124705491d2cSKalle Valo 	entry = brcmf_skbcb(p)->mac;
124805491d2cSKalle Valo 	if (entry == NULL) {
1249dcb1471bSRafał Miłecki 		bphy_err(drvr, "no mac descriptor found for skb %p\n", p);
125005491d2cSKalle Valo 		return -ENOENT;
125105491d2cSKalle Valo 	}
125205491d2cSKalle Valo 
125305491d2cSKalle Valo 	brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p);
125405491d2cSKalle Valo 	if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
125505491d2cSKalle Valo 		prec += 1;
125605491d2cSKalle Valo 		qfull_stat = &fws->stats.supprq_full_error;
125705491d2cSKalle Valo 
125805491d2cSKalle Valo 		/* Fix out of order delivery of frames. Dont assume frame    */
125905491d2cSKalle Valo 		/* can be inserted at the end, but look for correct position */
126005491d2cSKalle Valo 		pq = &entry->psq;
126105491d2cSKalle Valo 		if (pktq_full(pq) || pktq_pfull(pq, prec)) {
126205491d2cSKalle Valo 			*qfull_stat += 1;
126305491d2cSKalle Valo 			return -ENFILE;
126405491d2cSKalle Valo 		}
126505491d2cSKalle Valo 		queue = &pq->q[prec].skblist;
126605491d2cSKalle Valo 
126705491d2cSKalle Valo 		p_head = skb_peek(queue);
126805491d2cSKalle Valo 		p_tail = skb_peek_tail(queue);
126905491d2cSKalle Valo 		fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN);
127005491d2cSKalle Valo 
127105491d2cSKalle Valo 		while (p_head != p_tail) {
127205491d2cSKalle Valo 			fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
127305491d2cSKalle Valo 								  FREERUN);
127405491d2cSKalle Valo 			/* be sure to handle wrap of 256 */
127505491d2cSKalle Valo 			if (((fr_new > fr_compare) &&
127605491d2cSKalle Valo 			     ((fr_new - fr_compare) < 128)) ||
127705491d2cSKalle Valo 			    ((fr_new < fr_compare) &&
127805491d2cSKalle Valo 			     ((fr_compare - fr_new) > 128)))
127905491d2cSKalle Valo 				break;
128005491d2cSKalle Valo 			p_tail = skb_queue_prev(queue, p_tail);
128105491d2cSKalle Valo 		}
128205491d2cSKalle Valo 		/* Position found. Determine what to do */
128305491d2cSKalle Valo 		if (p_tail == NULL) {
128405491d2cSKalle Valo 			/* empty list */
128505491d2cSKalle Valo 			__skb_queue_tail(queue, p);
128605491d2cSKalle Valo 		} else {
128705491d2cSKalle Valo 			fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
128805491d2cSKalle Valo 								  FREERUN);
128905491d2cSKalle Valo 			if (((fr_new > fr_compare) &&
129005491d2cSKalle Valo 			     ((fr_new - fr_compare) < 128)) ||
129105491d2cSKalle Valo 			    ((fr_new < fr_compare) &&
129205491d2cSKalle Valo 			     ((fr_compare - fr_new) > 128))) {
129305491d2cSKalle Valo 				/* After tail */
129405491d2cSKalle Valo 				__skb_queue_after(queue, p_tail, p);
129505491d2cSKalle Valo 			} else {
129605491d2cSKalle Valo 				/* Before tail */
129705491d2cSKalle Valo 				__skb_insert(p, p_tail->prev, p_tail, queue);
129805491d2cSKalle Valo 			}
129905491d2cSKalle Valo 		}
130005491d2cSKalle Valo 
130105491d2cSKalle Valo 		/* Complete the counters and statistics */
130205491d2cSKalle Valo 		pq->len++;
130305491d2cSKalle Valo 		if (pq->hi_prec < prec)
130405491d2cSKalle Valo 			pq->hi_prec = (u8) prec;
130505491d2cSKalle Valo 	} else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) {
130605491d2cSKalle Valo 		*qfull_stat += 1;
130705491d2cSKalle Valo 		return -ENFILE;
130805491d2cSKalle Valo 	}
130905491d2cSKalle Valo 
131005491d2cSKalle Valo 	/* increment total enqueued packet count */
131105491d2cSKalle Valo 	fws->fifo_delay_map |= 1 << fifo;
131205491d2cSKalle Valo 	fws->fifo_enqpkt[fifo]++;
131305491d2cSKalle Valo 
131405491d2cSKalle Valo 	/* update the sk_buff state */
131505491d2cSKalle Valo 	brcmf_skbcb(p)->state = state;
131605491d2cSKalle Valo 
131705491d2cSKalle Valo 	/*
131805491d2cSKalle Valo 	 * A packet has been pushed so update traffic
131905491d2cSKalle Valo 	 * availability bitmap, if applicable
132005491d2cSKalle Valo 	 */
132105491d2cSKalle Valo 	brcmf_fws_tim_update(fws, entry, fifo, true);
132205491d2cSKalle Valo 	brcmf_fws_flow_control_check(fws, &entry->psq,
132305491d2cSKalle Valo 				     brcmf_skb_if_flags_get_field(p, INDEX));
132405491d2cSKalle Valo 	return 0;
132505491d2cSKalle Valo }
132605491d2cSKalle Valo 
brcmf_fws_deq(struct brcmf_fws_info * fws,int fifo)132705491d2cSKalle Valo static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
132805491d2cSKalle Valo {
132905491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *table;
133005491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry;
133105491d2cSKalle Valo 	struct sk_buff *p;
133205491d2cSKalle Valo 	int num_nodes;
133305491d2cSKalle Valo 	int node_pos;
133405491d2cSKalle Valo 	int prec_out;
133505491d2cSKalle Valo 	int pmsk;
133605491d2cSKalle Valo 	int i;
133705491d2cSKalle Valo 
133805491d2cSKalle Valo 	table = (struct brcmf_fws_mac_descriptor *)&fws->desc;
133905491d2cSKalle Valo 	num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor);
134005491d2cSKalle Valo 	node_pos = fws->deq_node_pos[fifo];
134105491d2cSKalle Valo 
134205491d2cSKalle Valo 	for (i = 0; i < num_nodes; i++) {
134305491d2cSKalle Valo 		entry = &table[(node_pos + i) % num_nodes];
134405491d2cSKalle Valo 		if (!entry->occupied ||
134505491d2cSKalle Valo 		    brcmf_fws_macdesc_closed(fws, entry, fifo))
134605491d2cSKalle Valo 			continue;
134705491d2cSKalle Valo 
134805491d2cSKalle Valo 		if (entry->suppressed)
134905491d2cSKalle Valo 			pmsk = 2;
135005491d2cSKalle Valo 		else
135105491d2cSKalle Valo 			pmsk = 3;
135205491d2cSKalle Valo 		p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
135305491d2cSKalle Valo 		if (p == NULL) {
135405491d2cSKalle Valo 			if (entry->suppressed) {
135505491d2cSKalle Valo 				if (entry->suppr_transit_count)
135605491d2cSKalle Valo 					continue;
135705491d2cSKalle Valo 				entry->suppressed = false;
135805491d2cSKalle Valo 				p = brcmu_pktq_mdeq(&entry->psq,
135905491d2cSKalle Valo 						    1 << (fifo * 2), &prec_out);
136005491d2cSKalle Valo 			}
136105491d2cSKalle Valo 		}
136205491d2cSKalle Valo 		if  (p == NULL)
136305491d2cSKalle Valo 			continue;
136405491d2cSKalle Valo 
136505491d2cSKalle Valo 		brcmf_fws_macdesc_use_req_credit(entry, p);
136605491d2cSKalle Valo 
136705491d2cSKalle Valo 		/* move dequeue position to ensure fair round-robin */
136805491d2cSKalle Valo 		fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes;
136905491d2cSKalle Valo 		brcmf_fws_flow_control_check(fws, &entry->psq,
137005491d2cSKalle Valo 					     brcmf_skb_if_flags_get_field(p,
137105491d2cSKalle Valo 									  INDEX)
137205491d2cSKalle Valo 					     );
137305491d2cSKalle Valo 		/*
137405491d2cSKalle Valo 		 * A packet has been picked up, update traffic
137505491d2cSKalle Valo 		 * availability bitmap, if applicable
137605491d2cSKalle Valo 		 */
137705491d2cSKalle Valo 		brcmf_fws_tim_update(fws, entry, fifo, false);
137805491d2cSKalle Valo 
137905491d2cSKalle Valo 		/*
138005491d2cSKalle Valo 		 * decrement total enqueued fifo packets and
138105491d2cSKalle Valo 		 * clear delay bitmap if done.
138205491d2cSKalle Valo 		 */
138305491d2cSKalle Valo 		fws->fifo_enqpkt[fifo]--;
138405491d2cSKalle Valo 		if (fws->fifo_enqpkt[fifo] == 0)
138505491d2cSKalle Valo 			fws->fifo_delay_map &= ~(1 << fifo);
138605491d2cSKalle Valo 		goto done;
138705491d2cSKalle Valo 	}
138805491d2cSKalle Valo 	p = NULL;
138905491d2cSKalle Valo done:
139005491d2cSKalle Valo 	brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);
139105491d2cSKalle Valo 	return p;
139205491d2cSKalle Valo }
139305491d2cSKalle Valo 
brcmf_fws_txstatus_suppressed(struct brcmf_fws_info * fws,int fifo,struct sk_buff * skb,u32 genbit,u16 seq)139405491d2cSKalle Valo static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
139505491d2cSKalle Valo 					 struct sk_buff *skb,
139605491d2cSKalle Valo 					 u32 genbit, u16 seq)
139705491d2cSKalle Valo {
139805491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
139905491d2cSKalle Valo 	u32 hslot;
140005491d2cSKalle Valo 	int ret;
140105491d2cSKalle Valo 
140205491d2cSKalle Valo 	hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
140305491d2cSKalle Valo 
140405491d2cSKalle Valo 	/* this packet was suppressed */
140505491d2cSKalle Valo 	if (!entry->suppressed) {
140605491d2cSKalle Valo 		entry->suppressed = true;
140705491d2cSKalle Valo 		entry->suppr_transit_count = entry->transit_count;
140805491d2cSKalle Valo 		brcmf_dbg(DATA, "suppress %s: transit %d\n",
140905491d2cSKalle Valo 			  entry->name, entry->transit_count);
141005491d2cSKalle Valo 	}
141105491d2cSKalle Valo 
141205491d2cSKalle Valo 	entry->generation = genbit;
141305491d2cSKalle Valo 
141405491d2cSKalle Valo 	brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
141505491d2cSKalle Valo 	brcmf_skbcb(skb)->htod_seq = seq;
141605491d2cSKalle Valo 	if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
141705491d2cSKalle Valo 		brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
141805491d2cSKalle Valo 		brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
141905491d2cSKalle Valo 	} else {
142005491d2cSKalle Valo 		brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
142105491d2cSKalle Valo 	}
142205491d2cSKalle Valo 	ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
142305491d2cSKalle Valo 
142405491d2cSKalle Valo 	if (ret != 0) {
142505491d2cSKalle Valo 		/* suppress q is full drop this packet */
142605491d2cSKalle Valo 		brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true);
142705491d2cSKalle Valo 	} else {
142805491d2cSKalle Valo 		/* Mark suppressed to avoid a double free during wlfc cleanup */
142905491d2cSKalle Valo 		brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
143005491d2cSKalle Valo 	}
143105491d2cSKalle Valo 
143205491d2cSKalle Valo 	return ret;
143305491d2cSKalle Valo }
143405491d2cSKalle Valo 
143505491d2cSKalle Valo static int
brcmf_fws_txs_process(struct brcmf_fws_info * fws,u8 flags,u32 hslot,u32 genbit,u16 seq,u8 compcnt)143605491d2cSKalle Valo brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
1437e4af3ffbSChung-Hsien Hsu 		      u32 genbit, u16 seq, u8 compcnt)
143805491d2cSKalle Valo {
1439dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = fws->drvr;
144005491d2cSKalle Valo 	u32 fifo;
1441e4af3ffbSChung-Hsien Hsu 	u8 cnt = 0;
144205491d2cSKalle Valo 	int ret;
144305491d2cSKalle Valo 	bool remove_from_hanger = true;
144405491d2cSKalle Valo 	struct sk_buff *skb;
144505491d2cSKalle Valo 	struct brcmf_skbuff_cb *skcb;
144605491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry = NULL;
144705491d2cSKalle Valo 	struct brcmf_if *ifp;
144805491d2cSKalle Valo 
144905491d2cSKalle Valo 	brcmf_dbg(DATA, "flags %d\n", flags);
145005491d2cSKalle Valo 
145105491d2cSKalle Valo 	if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
1452e4af3ffbSChung-Hsien Hsu 		fws->stats.txs_discard += compcnt;
145305491d2cSKalle Valo 	else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) {
1454e4af3ffbSChung-Hsien Hsu 		fws->stats.txs_supp_core += compcnt;
145505491d2cSKalle Valo 		remove_from_hanger = false;
145605491d2cSKalle Valo 	} else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) {
1457e4af3ffbSChung-Hsien Hsu 		fws->stats.txs_supp_ps += compcnt;
145805491d2cSKalle Valo 		remove_from_hanger = false;
145905491d2cSKalle Valo 	} else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)
1460e4af3ffbSChung-Hsien Hsu 		fws->stats.txs_tossed += compcnt;
1461d843246eSChung-Hsien Hsu 	else if (flags == BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK)
1462d843246eSChung-Hsien Hsu 		fws->stats.txs_discard += compcnt;
1463d843246eSChung-Hsien Hsu 	else if (flags == BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED)
1464d843246eSChung-Hsien Hsu 		fws->stats.txs_discard += compcnt;
146505491d2cSKalle Valo 	else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)
1466e4af3ffbSChung-Hsien Hsu 		fws->stats.txs_host_tossed += compcnt;
146705491d2cSKalle Valo 	else
1468dcb1471bSRafał Miłecki 		bphy_err(drvr, "unexpected txstatus\n");
146905491d2cSKalle Valo 
1470e4af3ffbSChung-Hsien Hsu 	while (cnt < compcnt) {
147105491d2cSKalle Valo 		ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
147205491d2cSKalle Valo 					      remove_from_hanger);
147305491d2cSKalle Valo 		if (ret != 0) {
1474dcb1471bSRafał Miłecki 			bphy_err(drvr, "no packet in hanger slot: hslot=%d\n",
1475e4af3ffbSChung-Hsien Hsu 				 hslot);
1476e4af3ffbSChung-Hsien Hsu 			goto cont;
147705491d2cSKalle Valo 		}
147805491d2cSKalle Valo 
147905491d2cSKalle Valo 		skcb = brcmf_skbcb(skb);
148005491d2cSKalle Valo 		entry = skcb->mac;
148105491d2cSKalle Valo 		if (WARN_ON(!entry)) {
148205491d2cSKalle Valo 			brcmu_pkt_buf_free_skb(skb);
1483e4af3ffbSChung-Hsien Hsu 			goto cont;
148405491d2cSKalle Valo 		}
148505491d2cSKalle Valo 		entry->transit_count--;
148605491d2cSKalle Valo 		if (entry->suppressed && entry->suppr_transit_count)
148705491d2cSKalle Valo 			entry->suppr_transit_count--;
148805491d2cSKalle Valo 
1489e4af3ffbSChung-Hsien Hsu 		brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name,
1490e4af3ffbSChung-Hsien Hsu 			  flags, skcb->htod, seq);
149105491d2cSKalle Valo 
149205491d2cSKalle Valo 		/* pick up the implicit credit from this packet */
149305491d2cSKalle Valo 		fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
1494e4af3ffbSChung-Hsien Hsu 		if (fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT ||
149505491d2cSKalle Valo 		    (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
1496e4af3ffbSChung-Hsien Hsu 		    flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED) {
149705491d2cSKalle Valo 			brcmf_fws_return_credits(fws, fifo, 1);
149805491d2cSKalle Valo 			brcmf_fws_schedule_deq(fws);
149905491d2cSKalle Valo 		}
150005491d2cSKalle Valo 		brcmf_fws_macdesc_return_req_credit(skb);
150105491d2cSKalle Valo 
150205491d2cSKalle Valo 		ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
150305491d2cSKalle Valo 		if (ret) {
150405491d2cSKalle Valo 			brcmu_pkt_buf_free_skb(skb);
1505e4af3ffbSChung-Hsien Hsu 			goto cont;
150605491d2cSKalle Valo 		}
150705491d2cSKalle Valo 		if (!remove_from_hanger)
150805491d2cSKalle Valo 			ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
150905491d2cSKalle Valo 							    genbit, seq);
151005491d2cSKalle Valo 		if (remove_from_hanger || ret)
151105491d2cSKalle Valo 			brcmf_txfinalize(ifp, skb, true);
151205491d2cSKalle Valo 
1513e4af3ffbSChung-Hsien Hsu cont:
1514e4af3ffbSChung-Hsien Hsu 		hslot = (hslot + 1) & (BRCMF_FWS_TXSTAT_HSLOT_MASK >>
1515e4af3ffbSChung-Hsien Hsu 				       BRCMF_FWS_TXSTAT_HSLOT_SHIFT);
1516e4af3ffbSChung-Hsien Hsu 		if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
1517e4af3ffbSChung-Hsien Hsu 			seq = (seq + 1) & BRCMF_SKB_HTOD_SEQ_NR_MASK;
1518e4af3ffbSChung-Hsien Hsu 
1519e4af3ffbSChung-Hsien Hsu 		cnt++;
1520e4af3ffbSChung-Hsien Hsu 	}
1521e4af3ffbSChung-Hsien Hsu 
152205491d2cSKalle Valo 	return 0;
152305491d2cSKalle Valo }
152405491d2cSKalle Valo 
brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info * fws,u8 * data)152505491d2cSKalle Valo static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
152605491d2cSKalle Valo 					     u8 *data)
152705491d2cSKalle Valo {
152805491d2cSKalle Valo 	int i;
152905491d2cSKalle Valo 
153005491d2cSKalle Valo 	if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
153105491d2cSKalle Valo 		brcmf_dbg(INFO, "ignored\n");
153205491d2cSKalle Valo 		return BRCMF_FWS_RET_OK_NOSCHEDULE;
153305491d2cSKalle Valo 	}
153405491d2cSKalle Valo 
153505491d2cSKalle Valo 	brcmf_dbg(DATA, "enter: data %pM\n", data);
153605491d2cSKalle Valo 	brcmf_fws_lock(fws);
153705491d2cSKalle Valo 	for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
153805491d2cSKalle Valo 		brcmf_fws_return_credits(fws, i, data[i]);
153905491d2cSKalle Valo 
154005491d2cSKalle Valo 	brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
154105491d2cSKalle Valo 		  fws->fifo_delay_map);
154205491d2cSKalle Valo 	brcmf_fws_unlock(fws);
154305491d2cSKalle Valo 	return BRCMF_FWS_RET_OK_SCHEDULE;
154405491d2cSKalle Valo }
154505491d2cSKalle Valo 
brcmf_fws_txstatus_indicate(struct brcmf_fws_info * fws,u8 type,u8 * data)1546e4af3ffbSChung-Hsien Hsu static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 type,
1547e4af3ffbSChung-Hsien Hsu 				       u8 *data)
154805491d2cSKalle Valo {
154905491d2cSKalle Valo 	__le32 status_le;
155005491d2cSKalle Valo 	__le16 seq_le;
155105491d2cSKalle Valo 	u32 status;
155205491d2cSKalle Valo 	u32 hslot;
155305491d2cSKalle Valo 	u32 genbit;
155405491d2cSKalle Valo 	u8 flags;
155505491d2cSKalle Valo 	u16 seq;
1556e4af3ffbSChung-Hsien Hsu 	u8 compcnt;
1557e4af3ffbSChung-Hsien Hsu 	u8 compcnt_offset = BRCMF_FWS_TYPE_TXSTATUS_LEN;
155805491d2cSKalle Valo 
155905491d2cSKalle Valo 	memcpy(&status_le, data, sizeof(status_le));
156005491d2cSKalle Valo 	status = le32_to_cpu(status_le);
156105491d2cSKalle Valo 	flags = brcmf_txstatus_get_field(status, FLAGS);
156205491d2cSKalle Valo 	hslot = brcmf_txstatus_get_field(status, HSLOT);
156305491d2cSKalle Valo 	genbit = brcmf_txstatus_get_field(status, GENERATION);
156405491d2cSKalle Valo 	if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
1565e4af3ffbSChung-Hsien Hsu 		memcpy(&seq_le, &data[BRCMF_FWS_TYPE_TXSTATUS_LEN],
156605491d2cSKalle Valo 		       sizeof(seq_le));
156705491d2cSKalle Valo 		seq = le16_to_cpu(seq_le);
1568e4af3ffbSChung-Hsien Hsu 		compcnt_offset += BRCMF_FWS_TYPE_SEQ_LEN;
156905491d2cSKalle Valo 	} else {
157005491d2cSKalle Valo 		seq = 0;
157105491d2cSKalle Valo 	}
157205491d2cSKalle Valo 
1573e4af3ffbSChung-Hsien Hsu 	if (type == BRCMF_FWS_TYPE_COMP_TXSTATUS)
1574e4af3ffbSChung-Hsien Hsu 		compcnt = data[compcnt_offset];
1575e4af3ffbSChung-Hsien Hsu 	else
1576e4af3ffbSChung-Hsien Hsu 		compcnt = 1;
1577e4af3ffbSChung-Hsien Hsu 	fws->stats.txs_indicate += compcnt;
1578e4af3ffbSChung-Hsien Hsu 
157905491d2cSKalle Valo 	brcmf_fws_lock(fws);
1580e4af3ffbSChung-Hsien Hsu 	brcmf_fws_txs_process(fws, flags, hslot, genbit, seq, compcnt);
158105491d2cSKalle Valo 	brcmf_fws_unlock(fws);
158205491d2cSKalle Valo 	return BRCMF_FWS_RET_OK_NOSCHEDULE;
158305491d2cSKalle Valo }
158405491d2cSKalle Valo 
brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info * fws,u8 * data)158505491d2cSKalle Valo static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
158605491d2cSKalle Valo {
158705491d2cSKalle Valo 	__le32 timestamp;
158805491d2cSKalle Valo 
158905491d2cSKalle Valo 	memcpy(&timestamp, &data[2], sizeof(timestamp));
159005491d2cSKalle Valo 	brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],
159105491d2cSKalle Valo 		  le32_to_cpu(timestamp));
159205491d2cSKalle Valo 	return 0;
159305491d2cSKalle Valo }
159405491d2cSKalle Valo 
brcmf_fws_notify_credit_map(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)159505491d2cSKalle Valo static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
159605491d2cSKalle Valo 				       const struct brcmf_event_msg *e,
159705491d2cSKalle Valo 				       void *data)
159805491d2cSKalle Valo {
1599dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
1600dcb1471bSRafał Miłecki 	struct brcmf_fws_info *fws = drvr_to_fws(drvr);
160105491d2cSKalle Valo 	int i;
160205491d2cSKalle Valo 	u8 *credits = data;
160305491d2cSKalle Valo 
160405491d2cSKalle Valo 	if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
1605dcb1471bSRafał Miłecki 		bphy_err(drvr, "event payload too small (%d)\n", e->datalen);
160605491d2cSKalle Valo 		return -EINVAL;
160705491d2cSKalle Valo 	}
160805491d2cSKalle Valo 
160905491d2cSKalle Valo 	fws->creditmap_received = true;
161005491d2cSKalle Valo 
161105491d2cSKalle Valo 	brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
161205491d2cSKalle Valo 	brcmf_fws_lock(fws);
161305491d2cSKalle Valo 	for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
1614153e22c0SWright Feng 		fws->fifo_credit[i] += credits[i] - fws->init_fifo_credit[i];
1615153e22c0SWright Feng 		fws->init_fifo_credit[i] = credits[i];
1616153e22c0SWright Feng 		if (fws->fifo_credit[i] > 0)
161705491d2cSKalle Valo 			fws->fifo_credit_map |= 1 << i;
161805491d2cSKalle Valo 		else
161905491d2cSKalle Valo 			fws->fifo_credit_map &= ~(1 << i);
1620153e22c0SWright Feng 		WARN_ONCE(fws->fifo_credit[i] < 0,
1621153e22c0SWright Feng 			  "fifo_credit[%d] is negative(%d)\n", i,
1622153e22c0SWright Feng 			  fws->fifo_credit[i]);
162305491d2cSKalle Valo 	}
162405491d2cSKalle Valo 	brcmf_fws_schedule_deq(fws);
162505491d2cSKalle Valo 	brcmf_fws_unlock(fws);
162605491d2cSKalle Valo 	return 0;
162705491d2cSKalle Valo }
162805491d2cSKalle Valo 
brcmf_fws_notify_bcmc_credit_support(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)162905491d2cSKalle Valo static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
163005491d2cSKalle Valo 						const struct brcmf_event_msg *e,
163105491d2cSKalle Valo 						void *data)
163205491d2cSKalle Valo {
1633acf8ac41SArend Van Spriel 	struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
163405491d2cSKalle Valo 
1635b513cac2SColin Ian King 	if (fws) {
163605491d2cSKalle Valo 		brcmf_fws_lock(fws);
163705491d2cSKalle Valo 		fws->bcmc_credit_check = true;
163805491d2cSKalle Valo 		brcmf_fws_unlock(fws);
1639b513cac2SColin Ian King 	}
164005491d2cSKalle Valo 	return 0;
164105491d2cSKalle Valo }
164205491d2cSKalle Valo 
brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder * rfi,u8 start,u8 end,struct sk_buff_head * skb_list)1643bbd1f932SArend van Spriel static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
1644bbd1f932SArend van Spriel 					 u8 start, u8 end,
1645bbd1f932SArend van Spriel 					 struct sk_buff_head *skb_list)
1646bbd1f932SArend van Spriel {
1647bbd1f932SArend van Spriel 	/* initialize return list */
1648bbd1f932SArend van Spriel 	__skb_queue_head_init(skb_list);
1649bbd1f932SArend van Spriel 
1650bbd1f932SArend van Spriel 	if (rfi->pend_pkts == 0) {
1651bbd1f932SArend van Spriel 		brcmf_dbg(INFO, "no packets in reorder queue\n");
1652bbd1f932SArend van Spriel 		return;
1653bbd1f932SArend van Spriel 	}
1654bbd1f932SArend van Spriel 
1655bbd1f932SArend van Spriel 	do {
1656bbd1f932SArend van Spriel 		if (rfi->pktslots[start]) {
1657bbd1f932SArend van Spriel 			__skb_queue_tail(skb_list, rfi->pktslots[start]);
1658bbd1f932SArend van Spriel 			rfi->pktslots[start] = NULL;
1659bbd1f932SArend van Spriel 		}
1660bbd1f932SArend van Spriel 		start++;
1661bbd1f932SArend van Spriel 		if (start > rfi->max_idx)
1662bbd1f932SArend van Spriel 			start = 0;
1663bbd1f932SArend van Spriel 	} while (start != end);
1664bbd1f932SArend van Spriel 	rfi->pend_pkts -= skb_queue_len(skb_list);
1665bbd1f932SArend van Spriel }
1666bbd1f932SArend van Spriel 
brcmf_fws_rxreorder(struct brcmf_if * ifp,struct sk_buff * pkt)1667b381728eSSebastian Andrzej Siewior void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt)
1668bbd1f932SArend van Spriel {
1669dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
1670bbd1f932SArend van Spriel 	u8 *reorder_data;
1671bbd1f932SArend van Spriel 	u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
1672bbd1f932SArend van Spriel 	struct brcmf_ampdu_rx_reorder *rfi;
1673bbd1f932SArend van Spriel 	struct sk_buff_head reorder_list;
1674bbd1f932SArend van Spriel 	struct sk_buff *pnext;
1675bbd1f932SArend van Spriel 	u8 flags;
1676bbd1f932SArend van Spriel 	u32 buf_size;
1677bbd1f932SArend van Spriel 
1678bbd1f932SArend van Spriel 	reorder_data = ((struct brcmf_skb_reorder_data *)pkt->cb)->reorder;
1679bbd1f932SArend van Spriel 	flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
1680bbd1f932SArend van Spriel 	flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
1681bbd1f932SArend van Spriel 
1682bbd1f932SArend van Spriel 	/* validate flags and flow id */
1683bbd1f932SArend van Spriel 	if (flags == 0xFF) {
1684dcb1471bSRafał Miłecki 		bphy_err(drvr, "invalid flags...so ignore this packet\n");
1685b381728eSSebastian Andrzej Siewior 		brcmf_netif_rx(ifp, pkt);
1686bbd1f932SArend van Spriel 		return;
1687bbd1f932SArend van Spriel 	}
1688bbd1f932SArend van Spriel 
1689bbd1f932SArend van Spriel 	rfi = ifp->drvr->reorder_flows[flow_id];
1690bbd1f932SArend van Spriel 	if (flags & BRCMF_RXREORDER_DEL_FLOW) {
1691bbd1f932SArend van Spriel 		brcmf_dbg(INFO, "flow-%d: delete\n",
1692bbd1f932SArend van Spriel 			  flow_id);
1693bbd1f932SArend van Spriel 
1694bbd1f932SArend van Spriel 		if (rfi == NULL) {
1695bbd1f932SArend van Spriel 			brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
1696bbd1f932SArend van Spriel 				  flow_id);
1697b381728eSSebastian Andrzej Siewior 			brcmf_netif_rx(ifp, pkt);
1698bbd1f932SArend van Spriel 			return;
1699bbd1f932SArend van Spriel 		}
1700bbd1f932SArend van Spriel 
1701bbd1f932SArend van Spriel 		brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
1702bbd1f932SArend van Spriel 					     &reorder_list);
1703bbd1f932SArend van Spriel 		/* add the last packet */
1704bbd1f932SArend van Spriel 		__skb_queue_tail(&reorder_list, pkt);
1705bbd1f932SArend van Spriel 		kfree(rfi);
1706bbd1f932SArend van Spriel 		ifp->drvr->reorder_flows[flow_id] = NULL;
1707bbd1f932SArend van Spriel 		goto netif_rx;
1708bbd1f932SArend van Spriel 	}
1709bbd1f932SArend van Spriel 	/* from here on we need a flow reorder instance */
1710bbd1f932SArend van Spriel 	if (rfi == NULL) {
1711bbd1f932SArend van Spriel 		buf_size = sizeof(*rfi);
1712bbd1f932SArend van Spriel 		max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
1713bbd1f932SArend van Spriel 
1714bbd1f932SArend van Spriel 		buf_size += (max_idx + 1) * sizeof(pkt);
1715bbd1f932SArend van Spriel 
1716bbd1f932SArend van Spriel 		/* allocate space for flow reorder info */
1717bbd1f932SArend van Spriel 		brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
1718bbd1f932SArend van Spriel 			  flow_id, max_idx);
1719bbd1f932SArend van Spriel 		rfi = kzalloc(buf_size, GFP_ATOMIC);
1720bbd1f932SArend van Spriel 		if (rfi == NULL) {
1721dcb1471bSRafał Miłecki 			bphy_err(drvr, "failed to alloc buffer\n");
1722b381728eSSebastian Andrzej Siewior 			brcmf_netif_rx(ifp, pkt);
1723bbd1f932SArend van Spriel 			return;
1724bbd1f932SArend van Spriel 		}
1725bbd1f932SArend van Spriel 
1726bbd1f932SArend van Spriel 		ifp->drvr->reorder_flows[flow_id] = rfi;
1727bbd1f932SArend van Spriel 		rfi->pktslots = (struct sk_buff **)(rfi + 1);
1728bbd1f932SArend van Spriel 		rfi->max_idx = max_idx;
1729bbd1f932SArend van Spriel 	}
1730bbd1f932SArend van Spriel 	if (flags & BRCMF_RXREORDER_NEW_HOLE)  {
1731bbd1f932SArend van Spriel 		if (rfi->pend_pkts) {
1732bbd1f932SArend van Spriel 			brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
1733bbd1f932SArend van Spriel 						     rfi->exp_idx,
1734bbd1f932SArend van Spriel 						     &reorder_list);
1735bbd1f932SArend van Spriel 			WARN_ON(rfi->pend_pkts);
1736bbd1f932SArend van Spriel 		} else {
1737bbd1f932SArend van Spriel 			__skb_queue_head_init(&reorder_list);
1738bbd1f932SArend van Spriel 		}
1739bbd1f932SArend van Spriel 		rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
1740bbd1f932SArend van Spriel 		rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
1741bbd1f932SArend van Spriel 		rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
1742bbd1f932SArend van Spriel 		rfi->pktslots[rfi->cur_idx] = pkt;
1743bbd1f932SArend van Spriel 		rfi->pend_pkts++;
1744bbd1f932SArend van Spriel 		brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
1745bbd1f932SArend van Spriel 			  flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
1746bbd1f932SArend van Spriel 	} else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
1747bbd1f932SArend van Spriel 		cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
1748bbd1f932SArend van Spriel 		exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
1749bbd1f932SArend van Spriel 
1750bbd1f932SArend van Spriel 		if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
1751bbd1f932SArend van Spriel 			/* still in the current hole */
1752bbd1f932SArend van Spriel 			/* enqueue the current on the buffer chain */
1753bbd1f932SArend van Spriel 			if (rfi->pktslots[cur_idx] != NULL) {
1754bbd1f932SArend van Spriel 				brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
1755bbd1f932SArend van Spriel 				brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
1756bbd1f932SArend van Spriel 				rfi->pktslots[cur_idx] = NULL;
1757bbd1f932SArend van Spriel 			}
1758bbd1f932SArend van Spriel 			rfi->pktslots[cur_idx] = pkt;
1759bbd1f932SArend van Spriel 			rfi->pend_pkts++;
1760bbd1f932SArend van Spriel 			rfi->cur_idx = cur_idx;
1761bbd1f932SArend van Spriel 			brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
1762bbd1f932SArend van Spriel 				  flow_id, cur_idx, exp_idx, rfi->pend_pkts);
1763bbd1f932SArend van Spriel 
1764bbd1f932SArend van Spriel 			/* can return now as there is no reorder
1765bbd1f932SArend van Spriel 			 * list to process.
1766bbd1f932SArend van Spriel 			 */
1767bbd1f932SArend van Spriel 			return;
1768bbd1f932SArend van Spriel 		}
1769bbd1f932SArend van Spriel 		if (rfi->exp_idx == cur_idx) {
1770bbd1f932SArend van Spriel 			if (rfi->pktslots[cur_idx] != NULL) {
1771bbd1f932SArend van Spriel 				brcmf_dbg(INFO, "error buffer pending..free it\n");
1772bbd1f932SArend van Spriel 				brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
1773bbd1f932SArend van Spriel 				rfi->pktslots[cur_idx] = NULL;
1774bbd1f932SArend van Spriel 			}
1775bbd1f932SArend van Spriel 			rfi->pktslots[cur_idx] = pkt;
1776bbd1f932SArend van Spriel 			rfi->pend_pkts++;
1777bbd1f932SArend van Spriel 
1778bbd1f932SArend van Spriel 			/* got the expected one. flush from current to expected
1779bbd1f932SArend van Spriel 			 * and update expected
1780bbd1f932SArend van Spriel 			 */
1781bbd1f932SArend van Spriel 			brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
1782bbd1f932SArend van Spriel 				  flow_id, cur_idx, exp_idx, rfi->pend_pkts);
1783bbd1f932SArend van Spriel 
1784bbd1f932SArend van Spriel 			rfi->cur_idx = cur_idx;
1785bbd1f932SArend van Spriel 			rfi->exp_idx = exp_idx;
1786bbd1f932SArend van Spriel 
1787bbd1f932SArend van Spriel 			brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
1788bbd1f932SArend van Spriel 						     &reorder_list);
1789bbd1f932SArend van Spriel 			brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
1790bbd1f932SArend van Spriel 				  flow_id, skb_queue_len(&reorder_list),
1791bbd1f932SArend van Spriel 				  rfi->pend_pkts);
1792bbd1f932SArend van Spriel 		} else {
1793bbd1f932SArend van Spriel 			u8 end_idx;
1794bbd1f932SArend van Spriel 
1795bbd1f932SArend van Spriel 			brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
1796bbd1f932SArend van Spriel 				  flow_id, flags, rfi->cur_idx, rfi->exp_idx,
1797bbd1f932SArend van Spriel 				  cur_idx, exp_idx);
1798bbd1f932SArend van Spriel 			if (flags & BRCMF_RXREORDER_FLUSH_ALL)
1799bbd1f932SArend van Spriel 				end_idx = rfi->exp_idx;
1800bbd1f932SArend van Spriel 			else
1801bbd1f932SArend van Spriel 				end_idx = exp_idx;
1802bbd1f932SArend van Spriel 
1803bbd1f932SArend van Spriel 			/* flush pkts first */
1804bbd1f932SArend van Spriel 			brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
1805bbd1f932SArend van Spriel 						     &reorder_list);
1806bbd1f932SArend van Spriel 
1807bbd1f932SArend van Spriel 			if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
1808bbd1f932SArend van Spriel 				__skb_queue_tail(&reorder_list, pkt);
1809bbd1f932SArend van Spriel 			} else {
1810bbd1f932SArend van Spriel 				rfi->pktslots[cur_idx] = pkt;
1811bbd1f932SArend van Spriel 				rfi->pend_pkts++;
1812bbd1f932SArend van Spriel 			}
1813bbd1f932SArend van Spriel 			rfi->exp_idx = exp_idx;
1814bbd1f932SArend van Spriel 			rfi->cur_idx = cur_idx;
1815bbd1f932SArend van Spriel 		}
1816bbd1f932SArend van Spriel 	} else {
1817bbd1f932SArend van Spriel 		/* explicity window move updating the expected index */
1818bbd1f932SArend van Spriel 		exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
1819bbd1f932SArend van Spriel 
1820bbd1f932SArend van Spriel 		brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
1821bbd1f932SArend van Spriel 			  flow_id, flags, rfi->exp_idx, exp_idx);
1822bbd1f932SArend van Spriel 		if (flags & BRCMF_RXREORDER_FLUSH_ALL)
1823bbd1f932SArend van Spriel 			end_idx =  rfi->exp_idx;
1824bbd1f932SArend van Spriel 		else
1825bbd1f932SArend van Spriel 			end_idx =  exp_idx;
1826bbd1f932SArend van Spriel 
1827bbd1f932SArend van Spriel 		brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
1828bbd1f932SArend van Spriel 					     &reorder_list);
1829bbd1f932SArend van Spriel 		__skb_queue_tail(&reorder_list, pkt);
1830bbd1f932SArend van Spriel 		/* set the new expected idx */
1831bbd1f932SArend van Spriel 		rfi->exp_idx = exp_idx;
1832bbd1f932SArend van Spriel 	}
1833bbd1f932SArend van Spriel netif_rx:
1834bbd1f932SArend van Spriel 	skb_queue_walk_safe(&reorder_list, pkt, pnext) {
1835bbd1f932SArend van Spriel 		__skb_unlink(pkt, &reorder_list);
1836b381728eSSebastian Andrzej Siewior 		brcmf_netif_rx(ifp, pkt);
1837bbd1f932SArend van Spriel 	}
1838bbd1f932SArend van Spriel }
1839bbd1f932SArend van Spriel 
brcmf_fws_hdrpull(struct brcmf_if * ifp,s16 siglen,struct sk_buff * skb)184005491d2cSKalle Valo void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
184105491d2cSKalle Valo {
184205491d2cSKalle Valo 	struct brcmf_skb_reorder_data *rd;
1843acf8ac41SArend Van Spriel 	struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
184405491d2cSKalle Valo 	u8 *signal_data;
184505491d2cSKalle Valo 	s16 data_len;
184605491d2cSKalle Valo 	u8 type;
184705491d2cSKalle Valo 	u8 len;
184805491d2cSKalle Valo 	u8 *data;
184905491d2cSKalle Valo 	s32 status;
185005491d2cSKalle Valo 	s32 err;
185105491d2cSKalle Valo 
185205491d2cSKalle Valo 	brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
185305491d2cSKalle Valo 		  ifp->ifidx, skb->len, siglen);
185405491d2cSKalle Valo 
185505491d2cSKalle Valo 	WARN_ON(siglen > skb->len);
185605491d2cSKalle Valo 
1857698bae2eSRaveendran Somu 	if (siglen > skb->len)
1858698bae2eSRaveendran Somu 		siglen = skb->len;
1859698bae2eSRaveendran Somu 
186005491d2cSKalle Valo 	if (!siglen)
186105491d2cSKalle Valo 		return;
186205491d2cSKalle Valo 	/* if flow control disabled, skip to packet data and leave */
186305491d2cSKalle Valo 	if ((!fws) || (!fws->fw_signals)) {
186405491d2cSKalle Valo 		skb_pull(skb, siglen);
186505491d2cSKalle Valo 		return;
186605491d2cSKalle Valo 	}
186705491d2cSKalle Valo 
186805491d2cSKalle Valo 	fws->stats.header_pulls++;
186905491d2cSKalle Valo 	data_len = siglen;
187005491d2cSKalle Valo 	signal_data = skb->data;
187105491d2cSKalle Valo 
187205491d2cSKalle Valo 	status = BRCMF_FWS_RET_OK_NOSCHEDULE;
187305491d2cSKalle Valo 	while (data_len > 0) {
187405491d2cSKalle Valo 		/* extract tlv info */
187505491d2cSKalle Valo 		type = signal_data[0];
187605491d2cSKalle Valo 
187705491d2cSKalle Valo 		/* FILLER type is actually not a TLV, but
187805491d2cSKalle Valo 		 * a single byte that can be skipped.
187905491d2cSKalle Valo 		 */
188005491d2cSKalle Valo 		if (type == BRCMF_FWS_TYPE_FILLER) {
188105491d2cSKalle Valo 			signal_data += 1;
188205491d2cSKalle Valo 			data_len -= 1;
188305491d2cSKalle Valo 			continue;
188405491d2cSKalle Valo 		}
188505491d2cSKalle Valo 		len = signal_data[1];
188605491d2cSKalle Valo 		data = signal_data + 2;
188705491d2cSKalle Valo 
188805491d2cSKalle Valo 		brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n",
188905491d2cSKalle Valo 			  brcmf_fws_get_tlv_name(type), type, len,
189005491d2cSKalle Valo 			  brcmf_fws_get_tlv_len(fws, type));
189105491d2cSKalle Valo 
189205491d2cSKalle Valo 		/* abort parsing when length invalid */
189305491d2cSKalle Valo 		if (data_len < len + 2)
189405491d2cSKalle Valo 			break;
189505491d2cSKalle Valo 
189605491d2cSKalle Valo 		if (len < brcmf_fws_get_tlv_len(fws, type))
189705491d2cSKalle Valo 			break;
189805491d2cSKalle Valo 
189905491d2cSKalle Valo 		err = BRCMF_FWS_RET_OK_NOSCHEDULE;
190005491d2cSKalle Valo 		switch (type) {
190105491d2cSKalle Valo 		case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
190205491d2cSKalle Valo 			rd = (struct brcmf_skb_reorder_data *)skb->cb;
190305491d2cSKalle Valo 			rd->reorder = data;
190405491d2cSKalle Valo 			break;
190505491d2cSKalle Valo 		case BRCMF_FWS_TYPE_MACDESC_ADD:
190605491d2cSKalle Valo 		case BRCMF_FWS_TYPE_MACDESC_DEL:
190705491d2cSKalle Valo 			brcmf_fws_macdesc_indicate(fws, type, data);
190805491d2cSKalle Valo 			break;
190905491d2cSKalle Valo 		case BRCMF_FWS_TYPE_MAC_OPEN:
191005491d2cSKalle Valo 		case BRCMF_FWS_TYPE_MAC_CLOSE:
191105491d2cSKalle Valo 			err = brcmf_fws_macdesc_state_indicate(fws, type, data);
191205491d2cSKalle Valo 			break;
191305491d2cSKalle Valo 		case BRCMF_FWS_TYPE_INTERFACE_OPEN:
191405491d2cSKalle Valo 		case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
191505491d2cSKalle Valo 			err = brcmf_fws_interface_state_indicate(fws, type,
191605491d2cSKalle Valo 								 data);
191705491d2cSKalle Valo 			break;
191805491d2cSKalle Valo 		case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
191905491d2cSKalle Valo 		case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
192005491d2cSKalle Valo 			err = brcmf_fws_request_indicate(fws, type, data);
192105491d2cSKalle Valo 			break;
192205491d2cSKalle Valo 		case BRCMF_FWS_TYPE_TXSTATUS:
1923e4af3ffbSChung-Hsien Hsu 		case BRCMF_FWS_TYPE_COMP_TXSTATUS:
1924e4af3ffbSChung-Hsien Hsu 			brcmf_fws_txstatus_indicate(fws, type, data);
192505491d2cSKalle Valo 			break;
192605491d2cSKalle Valo 		case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
192705491d2cSKalle Valo 			err = brcmf_fws_fifocreditback_indicate(fws, data);
192805491d2cSKalle Valo 			break;
192905491d2cSKalle Valo 		case BRCMF_FWS_TYPE_RSSI:
193005491d2cSKalle Valo 			brcmf_fws_rssi_indicate(fws, *data);
193105491d2cSKalle Valo 			break;
193205491d2cSKalle Valo 		case BRCMF_FWS_TYPE_TRANS_ID:
193305491d2cSKalle Valo 			brcmf_fws_dbg_seqnum_check(fws, data);
193405491d2cSKalle Valo 			break;
193505491d2cSKalle Valo 		case BRCMF_FWS_TYPE_PKTTAG:
193605491d2cSKalle Valo 		case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP:
193705491d2cSKalle Valo 		default:
193805491d2cSKalle Valo 			fws->stats.tlv_invalid_type++;
193905491d2cSKalle Valo 			break;
194005491d2cSKalle Valo 		}
194105491d2cSKalle Valo 		if (err == BRCMF_FWS_RET_OK_SCHEDULE)
194205491d2cSKalle Valo 			status = BRCMF_FWS_RET_OK_SCHEDULE;
194305491d2cSKalle Valo 		signal_data += len + 2;
194405491d2cSKalle Valo 		data_len -= len + 2;
194505491d2cSKalle Valo 	}
194605491d2cSKalle Valo 
194705491d2cSKalle Valo 	if (data_len != 0)
194805491d2cSKalle Valo 		fws->stats.tlv_parse_failed++;
194905491d2cSKalle Valo 
195005491d2cSKalle Valo 	if (status == BRCMF_FWS_RET_OK_SCHEDULE)
195105491d2cSKalle Valo 		brcmf_fws_schedule_deq(fws);
195205491d2cSKalle Valo 
195305491d2cSKalle Valo 	/* signalling processing result does
195405491d2cSKalle Valo 	 * not affect the actual ethernet packet.
195505491d2cSKalle Valo 	 */
195605491d2cSKalle Valo 	skb_pull(skb, siglen);
195705491d2cSKalle Valo 
195805491d2cSKalle Valo 	/* this may be a signal-only packet
195905491d2cSKalle Valo 	 */
196005491d2cSKalle Valo 	if (skb->len == 0)
196105491d2cSKalle Valo 		fws->stats.header_only_pkt++;
196205491d2cSKalle Valo }
196305491d2cSKalle Valo 
brcmf_fws_precommit_skb(struct brcmf_fws_info * fws,int fifo,struct sk_buff * p)196405491d2cSKalle Valo static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
196505491d2cSKalle Valo 				   struct sk_buff *p)
196605491d2cSKalle Valo {
196705491d2cSKalle Valo 	struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
196805491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry = skcb->mac;
196905491d2cSKalle Valo 	u8 flags;
197005491d2cSKalle Valo 
197105491d2cSKalle Valo 	if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED)
197205491d2cSKalle Valo 		brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);
197305491d2cSKalle Valo 	flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
197405491d2cSKalle Valo 	if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
197505491d2cSKalle Valo 		/*
197605491d2cSKalle Valo 		 * Indicate that this packet is being sent in response to an
197705491d2cSKalle Valo 		 * explicit request from the firmware side.
197805491d2cSKalle Valo 		 */
197905491d2cSKalle Valo 		flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
198005491d2cSKalle Valo 	}
198105491d2cSKalle Valo 	brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
198205491d2cSKalle Valo 	return brcmf_fws_hdrpush(fws, p);
198305491d2cSKalle Valo }
198405491d2cSKalle Valo 
brcmf_fws_rollback_toq(struct brcmf_fws_info * fws,struct sk_buff * skb,int fifo)198505491d2cSKalle Valo static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
198605491d2cSKalle Valo 				   struct sk_buff *skb, int fifo)
198705491d2cSKalle Valo {
1988dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = fws->drvr;
198905491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry;
199005491d2cSKalle Valo 	struct sk_buff *pktout;
199105491d2cSKalle Valo 	int qidx, hslot;
199205491d2cSKalle Valo 	int rc = 0;
199305491d2cSKalle Valo 
199405491d2cSKalle Valo 	entry = brcmf_skbcb(skb)->mac;
199505491d2cSKalle Valo 	if (entry->occupied) {
199605491d2cSKalle Valo 		qidx = 2 * fifo;
199705491d2cSKalle Valo 		if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
199805491d2cSKalle Valo 			qidx++;
199905491d2cSKalle Valo 
200005491d2cSKalle Valo 		pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb);
200105491d2cSKalle Valo 		if (pktout == NULL) {
2002dcb1471bSRafał Miłecki 			bphy_err(drvr, "%s queue %d full\n", entry->name, qidx);
200305491d2cSKalle Valo 			rc = -ENOSPC;
200405491d2cSKalle Valo 		}
200505491d2cSKalle Valo 	} else {
2006dcb1471bSRafał Miłecki 		bphy_err(drvr, "%s entry removed\n", entry->name);
200705491d2cSKalle Valo 		rc = -ENOENT;
200805491d2cSKalle Valo 	}
200905491d2cSKalle Valo 
201005491d2cSKalle Valo 	if (rc) {
201105491d2cSKalle Valo 		fws->stats.rollback_failed++;
201205491d2cSKalle Valo 		hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
201305491d2cSKalle Valo 		brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
2014e4af3ffbSChung-Hsien Hsu 				      hslot, 0, 0, 1);
201505491d2cSKalle Valo 	} else {
201605491d2cSKalle Valo 		fws->stats.rollback_success++;
201705491d2cSKalle Valo 		brcmf_fws_return_credits(fws, fifo, 1);
201805491d2cSKalle Valo 		brcmf_fws_macdesc_return_req_credit(skb);
201905491d2cSKalle Valo 	}
202005491d2cSKalle Valo }
202105491d2cSKalle Valo 
brcmf_fws_borrow_credit(struct brcmf_fws_info * fws,int highest_lender_ac,int borrower_ac,bool borrow_all)2022683608bdSRaveendran Somu static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws,
2023683608bdSRaveendran Somu 				   int highest_lender_ac, int borrower_ac,
2024683608bdSRaveendran Somu 				   bool borrow_all)
202505491d2cSKalle Valo {
2026683608bdSRaveendran Somu 	int lender_ac, borrow_limit = 0;
202705491d2cSKalle Valo 
2028683608bdSRaveendran Somu 	for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {
202905491d2cSKalle Valo 
2030683608bdSRaveendran Somu 		if (!borrow_all)
2031683608bdSRaveendran Somu 			borrow_limit =
2032683608bdSRaveendran Somu 			  fws->init_fifo_credit[lender_ac] / BRCMF_BORROW_RATIO;
2033683608bdSRaveendran Somu 		else
2034683608bdSRaveendran Somu 			borrow_limit = 0;
2035683608bdSRaveendran Somu 
2036683608bdSRaveendran Somu 		if (fws->fifo_credit[lender_ac] > borrow_limit) {
2037683608bdSRaveendran Somu 			fws->credits_borrowed[borrower_ac][lender_ac]++;
203805491d2cSKalle Valo 			fws->fifo_credit[lender_ac]--;
203905491d2cSKalle Valo 			if (fws->fifo_credit[lender_ac] == 0)
204005491d2cSKalle Valo 				fws->fifo_credit_map &= ~(1 << lender_ac);
2041683608bdSRaveendran Somu 			fws->fifo_credit_map |= (1 << borrower_ac);
204205491d2cSKalle Valo 			brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac);
204305491d2cSKalle Valo 			return 0;
204405491d2cSKalle Valo 		}
204505491d2cSKalle Valo 	}
2046683608bdSRaveendran Somu 	fws->fifo_credit_map &= ~(1 << borrower_ac);
204705491d2cSKalle Valo 	return -ENAVAIL;
204805491d2cSKalle Valo }
204905491d2cSKalle Valo 
brcmf_fws_commit_skb(struct brcmf_fws_info * fws,int fifo,struct sk_buff * skb)205005491d2cSKalle Valo static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
205105491d2cSKalle Valo 				struct sk_buff *skb)
205205491d2cSKalle Valo {
205305491d2cSKalle Valo 	struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
205405491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry;
205505491d2cSKalle Valo 	int rc;
205605491d2cSKalle Valo 	u8 ifidx;
205705491d2cSKalle Valo 	u8 data_offset;
205805491d2cSKalle Valo 
205905491d2cSKalle Valo 	entry = skcb->mac;
206005491d2cSKalle Valo 	if (IS_ERR(entry))
206105491d2cSKalle Valo 		return PTR_ERR(entry);
206205491d2cSKalle Valo 
206305491d2cSKalle Valo 	data_offset = brcmf_fws_precommit_skb(fws, fifo, skb);
206405491d2cSKalle Valo 	entry->transit_count++;
206505491d2cSKalle Valo 	if (entry->suppressed)
206605491d2cSKalle Valo 		entry->suppr_transit_count++;
206705491d2cSKalle Valo 	ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
206805491d2cSKalle Valo 	brcmf_fws_unlock(fws);
206905491d2cSKalle Valo 	rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
207005491d2cSKalle Valo 	brcmf_fws_lock(fws);
207105491d2cSKalle Valo 	brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
207205491d2cSKalle Valo 		  skcb->if_flags, skcb->htod, rc);
207305491d2cSKalle Valo 	if (rc < 0) {
207405491d2cSKalle Valo 		entry->transit_count--;
207505491d2cSKalle Valo 		if (entry->suppressed)
207605491d2cSKalle Valo 			entry->suppr_transit_count--;
207705491d2cSKalle Valo 		(void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
207805491d2cSKalle Valo 		goto rollback;
207905491d2cSKalle Valo 	}
208005491d2cSKalle Valo 
208105491d2cSKalle Valo 	fws->stats.pkt2bus++;
208205491d2cSKalle Valo 	fws->stats.send_pkts[fifo]++;
208305491d2cSKalle Valo 	if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
208405491d2cSKalle Valo 		fws->stats.requested_sent[fifo]++;
208505491d2cSKalle Valo 
208605491d2cSKalle Valo 	return rc;
208705491d2cSKalle Valo 
208805491d2cSKalle Valo rollback:
208905491d2cSKalle Valo 	brcmf_fws_rollback_toq(fws, skb, fifo);
209005491d2cSKalle Valo 	return rc;
209105491d2cSKalle Valo }
209205491d2cSKalle Valo 
brcmf_fws_assign_htod(struct brcmf_fws_info * fws,struct sk_buff * p,int fifo)209305491d2cSKalle Valo static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,
209405491d2cSKalle Valo 				  int fifo)
209505491d2cSKalle Valo {
209605491d2cSKalle Valo 	struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
209705491d2cSKalle Valo 	int rc, hslot;
209805491d2cSKalle Valo 
209905491d2cSKalle Valo 	skcb->htod = 0;
210005491d2cSKalle Valo 	skcb->htod_seq = 0;
210105491d2cSKalle Valo 	hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
210205491d2cSKalle Valo 	brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
210305491d2cSKalle Valo 	brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);
210405491d2cSKalle Valo 	brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
210505491d2cSKalle Valo 	rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
210605491d2cSKalle Valo 	if (!rc)
210705491d2cSKalle Valo 		skcb->mac->seq[fifo]++;
210805491d2cSKalle Valo 	else
210905491d2cSKalle Valo 		fws->stats.generic_error++;
211005491d2cSKalle Valo 	return rc;
211105491d2cSKalle Valo }
211205491d2cSKalle Valo 
brcmf_fws_process_skb(struct brcmf_if * ifp,struct sk_buff * skb)211305491d2cSKalle Valo int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
211405491d2cSKalle Valo {
2115dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
2116dcb1471bSRafał Miłecki 	struct brcmf_fws_info *fws = drvr_to_fws(drvr);
211705491d2cSKalle Valo 	struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
211805491d2cSKalle Valo 	struct ethhdr *eh = (struct ethhdr *)(skb->data);
211905491d2cSKalle Valo 	int fifo = BRCMF_FWS_FIFO_BCMC;
212005491d2cSKalle Valo 	bool multicast = is_multicast_ether_addr(eh->h_dest);
212105491d2cSKalle Valo 	int rc = 0;
212205491d2cSKalle Valo 
212305491d2cSKalle Valo 	brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
212405491d2cSKalle Valo 
212505491d2cSKalle Valo 	/* set control buffer information */
212605491d2cSKalle Valo 	skcb->if_flags = 0;
212705491d2cSKalle Valo 	skcb->state = BRCMF_FWS_SKBSTATE_NEW;
212805491d2cSKalle Valo 	brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
212992072e5fSSaravanan Shanmugham 
213092072e5fSSaravanan Shanmugham 	/* mapping from 802.1d priority to firmware fifo index */
213105491d2cSKalle Valo 	if (!multicast)
213292072e5fSSaravanan Shanmugham 		fifo = brcmf_map_prio_to_aci(drvr->config, skb->priority);
213305491d2cSKalle Valo 
213405491d2cSKalle Valo 	brcmf_fws_lock(fws);
213505491d2cSKalle Valo 	if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
213605491d2cSKalle Valo 		fws->borrow_defer_timestamp = jiffies +
213705491d2cSKalle Valo 					      BRCMF_FWS_BORROW_DEFER_PERIOD;
213805491d2cSKalle Valo 
213905491d2cSKalle Valo 	skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);
214005491d2cSKalle Valo 	brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,
214105491d2cSKalle Valo 		  eh->h_dest, multicast, fifo);
214205491d2cSKalle Valo 	if (!brcmf_fws_assign_htod(fws, skb, fifo)) {
214305491d2cSKalle Valo 		brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
214405491d2cSKalle Valo 		brcmf_fws_schedule_deq(fws);
214505491d2cSKalle Valo 	} else {
214678179869SRaveendran Somu 		bphy_err(drvr, "no hanger slot available\n");
214705491d2cSKalle Valo 		rc = -ENOMEM;
214805491d2cSKalle Valo 	}
214905491d2cSKalle Valo 	brcmf_fws_unlock(fws);
215005491d2cSKalle Valo 
215105491d2cSKalle Valo 	return rc;
215205491d2cSKalle Valo }
215305491d2cSKalle Valo 
brcmf_fws_reset_interface(struct brcmf_if * ifp)215405491d2cSKalle Valo void brcmf_fws_reset_interface(struct brcmf_if *ifp)
215505491d2cSKalle Valo {
215605491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
215705491d2cSKalle Valo 
215837a869ecSHante Meuleman 	brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
215905491d2cSKalle Valo 	if (!entry)
216005491d2cSKalle Valo 		return;
216105491d2cSKalle Valo 
216205491d2cSKalle Valo 	brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
216305491d2cSKalle Valo }
216405491d2cSKalle Valo 
brcmf_fws_add_interface(struct brcmf_if * ifp)216505491d2cSKalle Valo void brcmf_fws_add_interface(struct brcmf_if *ifp)
216605491d2cSKalle Valo {
2167acf8ac41SArend Van Spriel 	struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
216805491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry;
216905491d2cSKalle Valo 
2170a2b7a622SArend Van Spriel 	if (!ifp->ndev || !brcmf_fws_queue_skbs(fws))
217105491d2cSKalle Valo 		return;
217205491d2cSKalle Valo 
217305491d2cSKalle Valo 	entry = &fws->desc.iface[ifp->ifidx];
217405491d2cSKalle Valo 	ifp->fws_desc = entry;
217505491d2cSKalle Valo 	brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
217605491d2cSKalle Valo 	brcmf_fws_macdesc_set_name(fws, entry);
217705491d2cSKalle Valo 	brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
217805491d2cSKalle Valo 			BRCMF_FWS_PSQ_LEN);
217905491d2cSKalle Valo 	brcmf_dbg(TRACE, "added %s\n", entry->name);
218005491d2cSKalle Valo }
218105491d2cSKalle Valo 
brcmf_fws_del_interface(struct brcmf_if * ifp)218205491d2cSKalle Valo void brcmf_fws_del_interface(struct brcmf_if *ifp)
218305491d2cSKalle Valo {
218405491d2cSKalle Valo 	struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
2185acf8ac41SArend Van Spriel 	struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
218605491d2cSKalle Valo 
218705491d2cSKalle Valo 	if (!entry)
218805491d2cSKalle Valo 		return;
218905491d2cSKalle Valo 
2190acf8ac41SArend Van Spriel 	brcmf_fws_lock(fws);
219105491d2cSKalle Valo 	ifp->fws_desc = NULL;
219205491d2cSKalle Valo 	brcmf_dbg(TRACE, "deleting %s\n", entry->name);
2193c80d26e8SPiotr Figiel 	brcmf_fws_macdesc_cleanup(fws, &fws->desc.iface[ifp->ifidx],
2194c80d26e8SPiotr Figiel 				  ifp->ifidx);
219505491d2cSKalle Valo 	brcmf_fws_macdesc_deinit(entry);
2196acf8ac41SArend Van Spriel 	brcmf_fws_cleanup(fws, ifp->ifidx);
2197acf8ac41SArend Van Spriel 	brcmf_fws_unlock(fws);
219805491d2cSKalle Valo }
219905491d2cSKalle Valo 
brcmf_fws_dequeue_worker(struct work_struct * worker)220005491d2cSKalle Valo static void brcmf_fws_dequeue_worker(struct work_struct *worker)
220105491d2cSKalle Valo {
220205491d2cSKalle Valo 	struct brcmf_fws_info *fws;
220305491d2cSKalle Valo 	struct brcmf_pub *drvr;
220405491d2cSKalle Valo 	struct sk_buff *skb;
220505491d2cSKalle Valo 	int fifo;
220605491d2cSKalle Valo 	u32 hslot;
220705491d2cSKalle Valo 	u32 ifidx;
220805491d2cSKalle Valo 	int ret;
220905491d2cSKalle Valo 
221005491d2cSKalle Valo 	fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
221105491d2cSKalle Valo 	drvr = fws->drvr;
221205491d2cSKalle Valo 
221305491d2cSKalle Valo 	brcmf_fws_lock(fws);
221405491d2cSKalle Valo 	for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
221505491d2cSKalle Valo 	     fifo--) {
221605491d2cSKalle Valo 		if (!brcmf_fws_fc_active(fws)) {
221705491d2cSKalle Valo 			while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
221805491d2cSKalle Valo 				hslot = brcmf_skb_htod_tag_get_field(skb,
221905491d2cSKalle Valo 								     HSLOT);
222005491d2cSKalle Valo 				brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
222105491d2cSKalle Valo 							&skb, true);
222205491d2cSKalle Valo 				ifidx = brcmf_skb_if_flags_get_field(skb,
222305491d2cSKalle Valo 								     INDEX);
222405491d2cSKalle Valo 				/* Use proto layer to send data frame */
222505491d2cSKalle Valo 				brcmf_fws_unlock(fws);
222605491d2cSKalle Valo 				ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
222705491d2cSKalle Valo 				brcmf_fws_lock(fws);
222805491d2cSKalle Valo 				if (ret < 0)
222905491d2cSKalle Valo 					brcmf_txfinalize(brcmf_get_ifp(drvr,
223005491d2cSKalle Valo 								       ifidx),
223105491d2cSKalle Valo 							 skb, false);
223205491d2cSKalle Valo 				if (fws->bus_flow_blocked)
223305491d2cSKalle Valo 					break;
223405491d2cSKalle Valo 			}
223505491d2cSKalle Valo 			continue;
223605491d2cSKalle Valo 		}
2237683608bdSRaveendran Somu 
2238683608bdSRaveendran Somu 		while ((fws->fifo_credit[fifo]) ||
2239153e22c0SWright Feng 		       ((!fws->bcmc_credit_check) &&
224005491d2cSKalle Valo 				(fifo == BRCMF_FWS_FIFO_BCMC))) {
224105491d2cSKalle Valo 			skb = brcmf_fws_deq(fws, fifo);
224205491d2cSKalle Valo 			if (!skb)
224305491d2cSKalle Valo 				break;
224405491d2cSKalle Valo 			fws->fifo_credit[fifo]--;
224505491d2cSKalle Valo 			if (brcmf_fws_commit_skb(fws, fifo, skb))
224605491d2cSKalle Valo 				break;
224705491d2cSKalle Valo 			if (fws->bus_flow_blocked)
224805491d2cSKalle Valo 				break;
224905491d2cSKalle Valo 		}
2250683608bdSRaveendran Somu 
2251683608bdSRaveendran Somu 		if (fifo >= BRCMF_FWS_FIFO_AC_BE &&
2252683608bdSRaveendran Somu 		    fifo <= BRCMF_FWS_FIFO_AC_VO &&
2253683608bdSRaveendran Somu 		    fws->fifo_credit[fifo] == 0 &&
2254683608bdSRaveendran Somu 		    !fws->bus_flow_blocked) {
2255683608bdSRaveendran Somu 			while (brcmf_fws_borrow_credit(fws,
2256683608bdSRaveendran Somu 						       fifo - 1, fifo,
2257683608bdSRaveendran Somu 						       true) == 0) {
225805491d2cSKalle Valo 				skb = brcmf_fws_deq(fws, fifo);
225905491d2cSKalle Valo 				if (!skb) {
226005491d2cSKalle Valo 					brcmf_fws_return_credits(fws, fifo, 1);
226105491d2cSKalle Valo 					break;
226205491d2cSKalle Valo 				}
226305491d2cSKalle Valo 				if (brcmf_fws_commit_skb(fws, fifo, skb))
226405491d2cSKalle Valo 					break;
226505491d2cSKalle Valo 				if (fws->bus_flow_blocked)
226605491d2cSKalle Valo 					break;
226705491d2cSKalle Valo 			}
226805491d2cSKalle Valo 		}
226905491d2cSKalle Valo 	}
227005491d2cSKalle Valo 	brcmf_fws_unlock(fws);
227105491d2cSKalle Valo }
227205491d2cSKalle Valo 
227305491d2cSKalle Valo #ifdef DEBUG
brcmf_debugfs_fws_stats_read(struct seq_file * seq,void * data)227405491d2cSKalle Valo static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
227505491d2cSKalle Valo {
227605491d2cSKalle Valo 	struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
2277acf8ac41SArend Van Spriel 	struct brcmf_fws_stats *fwstats = &(drvr_to_fws(bus_if->drvr)->stats);
227805491d2cSKalle Valo 
227905491d2cSKalle Valo 	seq_printf(seq,
228005491d2cSKalle Valo 		   "header_pulls:      %u\n"
228105491d2cSKalle Valo 		   "header_only_pkt:   %u\n"
228205491d2cSKalle Valo 		   "tlv_parse_failed:  %u\n"
228305491d2cSKalle Valo 		   "tlv_invalid_type:  %u\n"
228405491d2cSKalle Valo 		   "mac_update_fails:  %u\n"
228505491d2cSKalle Valo 		   "ps_update_fails:   %u\n"
228605491d2cSKalle Valo 		   "if_update_fails:   %u\n"
228705491d2cSKalle Valo 		   "pkt2bus:           %u\n"
228805491d2cSKalle Valo 		   "generic_error:     %u\n"
228905491d2cSKalle Valo 		   "rollback_success:  %u\n"
229005491d2cSKalle Valo 		   "rollback_failed:   %u\n"
229105491d2cSKalle Valo 		   "delayq_full:       %u\n"
229205491d2cSKalle Valo 		   "supprq_full:       %u\n"
229305491d2cSKalle Valo 		   "txs_indicate:      %u\n"
229405491d2cSKalle Valo 		   "txs_discard:       %u\n"
229505491d2cSKalle Valo 		   "txs_suppr_core:    %u\n"
229605491d2cSKalle Valo 		   "txs_suppr_ps:      %u\n"
229705491d2cSKalle Valo 		   "txs_tossed:        %u\n"
229805491d2cSKalle Valo 		   "txs_host_tossed:   %u\n"
229905491d2cSKalle Valo 		   "bus_flow_block:    %u\n"
230005491d2cSKalle Valo 		   "fws_flow_block:    %u\n"
230105491d2cSKalle Valo 		   "send_pkts:         BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
230205491d2cSKalle Valo 		   "requested_sent:    BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
230305491d2cSKalle Valo 		   fwstats->header_pulls,
230405491d2cSKalle Valo 		   fwstats->header_only_pkt,
230505491d2cSKalle Valo 		   fwstats->tlv_parse_failed,
230605491d2cSKalle Valo 		   fwstats->tlv_invalid_type,
230705491d2cSKalle Valo 		   fwstats->mac_update_failed,
230805491d2cSKalle Valo 		   fwstats->mac_ps_update_failed,
230905491d2cSKalle Valo 		   fwstats->if_update_failed,
231005491d2cSKalle Valo 		   fwstats->pkt2bus,
231105491d2cSKalle Valo 		   fwstats->generic_error,
231205491d2cSKalle Valo 		   fwstats->rollback_success,
231305491d2cSKalle Valo 		   fwstats->rollback_failed,
231405491d2cSKalle Valo 		   fwstats->delayq_full_error,
231505491d2cSKalle Valo 		   fwstats->supprq_full_error,
231605491d2cSKalle Valo 		   fwstats->txs_indicate,
231705491d2cSKalle Valo 		   fwstats->txs_discard,
231805491d2cSKalle Valo 		   fwstats->txs_supp_core,
231905491d2cSKalle Valo 		   fwstats->txs_supp_ps,
232005491d2cSKalle Valo 		   fwstats->txs_tossed,
232105491d2cSKalle Valo 		   fwstats->txs_host_tossed,
232205491d2cSKalle Valo 		   fwstats->bus_flow_block,
232305491d2cSKalle Valo 		   fwstats->fws_flow_block,
232405491d2cSKalle Valo 		   fwstats->send_pkts[0], fwstats->send_pkts[1],
232505491d2cSKalle Valo 		   fwstats->send_pkts[2], fwstats->send_pkts[3],
232605491d2cSKalle Valo 		   fwstats->send_pkts[4],
232705491d2cSKalle Valo 		   fwstats->requested_sent[0],
232805491d2cSKalle Valo 		   fwstats->requested_sent[1],
232905491d2cSKalle Valo 		   fwstats->requested_sent[2],
233005491d2cSKalle Valo 		   fwstats->requested_sent[3],
233105491d2cSKalle Valo 		   fwstats->requested_sent[4]);
233205491d2cSKalle Valo 
233305491d2cSKalle Valo 	return 0;
233405491d2cSKalle Valo }
233505491d2cSKalle Valo #else
brcmf_debugfs_fws_stats_read(struct seq_file * seq,void * data)233605491d2cSKalle Valo static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
233705491d2cSKalle Valo {
233805491d2cSKalle Valo 	return 0;
233905491d2cSKalle Valo }
234005491d2cSKalle Valo #endif
234105491d2cSKalle Valo 
brcmf_fws_attach(struct brcmf_pub * drvr)2342acf8ac41SArend Van Spriel struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr)
234305491d2cSKalle Valo {
234405491d2cSKalle Valo 	struct brcmf_fws_info *fws;
234505491d2cSKalle Valo 	struct brcmf_if *ifp;
234605491d2cSKalle Valo 	u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
234705491d2cSKalle Valo 	int rc;
234805491d2cSKalle Valo 	u32 mode;
234905491d2cSKalle Valo 
2350acf8ac41SArend Van Spriel 	fws = kzalloc(sizeof(*fws), GFP_KERNEL);
2351acf8ac41SArend Van Spriel 	if (!fws) {
235205491d2cSKalle Valo 		rc = -ENOMEM;
235305491d2cSKalle Valo 		goto fail;
235405491d2cSKalle Valo 	}
235505491d2cSKalle Valo 
235605491d2cSKalle Valo 	spin_lock_init(&fws->spinlock);
235705491d2cSKalle Valo 
2358acf8ac41SArend Van Spriel 	/* store drvr reference */
235905491d2cSKalle Valo 	fws->drvr = drvr;
23607d34b056SHante Meuleman 	fws->fcmode = drvr->settings->fcmode;
236105491d2cSKalle Valo 
2362ff2af09fSJason Yan 	if (!drvr->bus_if->always_use_fws_queue &&
23637d34b056SHante Meuleman 	    (fws->fcmode == BRCMF_FWS_FCMODE_NONE)) {
236405491d2cSKalle Valo 		fws->avoid_queueing = true;
236505491d2cSKalle Valo 		brcmf_dbg(INFO, "FWS queueing will be avoided\n");
2366acf8ac41SArend Van Spriel 		return fws;
236705491d2cSKalle Valo 	}
236805491d2cSKalle Valo 
236905491d2cSKalle Valo 	fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
237005491d2cSKalle Valo 	if (fws->fws_wq == NULL) {
2371dcb1471bSRafał Miłecki 		bphy_err(drvr, "workqueue creation failed\n");
237205491d2cSKalle Valo 		rc = -EBADF;
237305491d2cSKalle Valo 		goto fail;
237405491d2cSKalle Valo 	}
237505491d2cSKalle Valo 	INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
237605491d2cSKalle Valo 
237705491d2cSKalle Valo 	/* enable firmware signalling if fcmode active */
237805491d2cSKalle Valo 	if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
237905491d2cSKalle Valo 		tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
238005491d2cSKalle Valo 		       BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
238105491d2cSKalle Valo 		       BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
238205491d2cSKalle Valo 		       BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;
238305491d2cSKalle Valo 
238405491d2cSKalle Valo 	rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
238505491d2cSKalle Valo 				 brcmf_fws_notify_credit_map);
238605491d2cSKalle Valo 	if (rc < 0) {
2387dcb1471bSRafał Miłecki 		bphy_err(drvr, "register credit map handler failed\n");
238805491d2cSKalle Valo 		goto fail;
238905491d2cSKalle Valo 	}
239005491d2cSKalle Valo 	rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT,
239105491d2cSKalle Valo 				 brcmf_fws_notify_bcmc_credit_support);
239205491d2cSKalle Valo 	if (rc < 0) {
2393dcb1471bSRafał Miłecki 		bphy_err(drvr, "register bcmc credit handler failed\n");
239405491d2cSKalle Valo 		brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
239505491d2cSKalle Valo 		goto fail;
239605491d2cSKalle Valo 	}
239705491d2cSKalle Valo 
239805491d2cSKalle Valo 	/* Setting the iovar may fail if feature is unsupported
239905491d2cSKalle Valo 	 * so leave the rc as is so driver initialization can
240005491d2cSKalle Valo 	 * continue. Set mode back to none indicating not enabled.
240105491d2cSKalle Valo 	 */
240205491d2cSKalle Valo 	fws->fw_signals = true;
240305491d2cSKalle Valo 	ifp = brcmf_get_ifp(drvr, 0);
240405491d2cSKalle Valo 	if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
2405dcb1471bSRafał Miłecki 		bphy_err(drvr, "failed to set bdcv2 tlv signaling\n");
240605491d2cSKalle Valo 		fws->fcmode = BRCMF_FWS_FCMODE_NONE;
240705491d2cSKalle Valo 		fws->fw_signals = false;
240805491d2cSKalle Valo 	}
240905491d2cSKalle Valo 
241005491d2cSKalle Valo 	if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
241105491d2cSKalle Valo 		brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
241205491d2cSKalle Valo 
241305491d2cSKalle Valo 	/* Enable seq number reuse, if supported */
241405491d2cSKalle Valo 	if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
241505491d2cSKalle Valo 		if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
241605491d2cSKalle Valo 			mode = 0;
241705491d2cSKalle Valo 			BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
241805491d2cSKalle Valo 			if (brcmf_fil_iovar_int_set(ifp,
241905491d2cSKalle Valo 						    "wlfc_mode", mode) == 0) {
242005491d2cSKalle Valo 				BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
242105491d2cSKalle Valo 			}
242205491d2cSKalle Valo 		}
242305491d2cSKalle Valo 	}
242405491d2cSKalle Valo 
242505491d2cSKalle Valo 	brcmf_fws_hanger_init(&fws->hanger);
242605491d2cSKalle Valo 	brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
242705491d2cSKalle Valo 	brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
2428acf8ac41SArend Van Spriel 	brcmf_dbg(INFO, "added %s\n", fws->desc.other.name);
242905491d2cSKalle Valo 	brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
243005491d2cSKalle Valo 			BRCMF_FWS_PSQ_LEN);
243105491d2cSKalle Valo 
243205491d2cSKalle Valo 	brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
243305491d2cSKalle Valo 		  fws->fw_signals ? "enabled" : "disabled", tlv);
2434acf8ac41SArend Van Spriel 	return fws;
243505491d2cSKalle Valo 
243605491d2cSKalle Valo fail:
2437a84a60ccSArend van Spriel 	brcmf_fws_detach(fws);
2438acf8ac41SArend Van Spriel 	return ERR_PTR(rc);
243905491d2cSKalle Valo }
244005491d2cSKalle Valo 
brcmf_fws_detach(struct brcmf_fws_info * fws)2441a84a60ccSArend van Spriel void brcmf_fws_detach(struct brcmf_fws_info *fws)
244205491d2cSKalle Valo {
244305491d2cSKalle Valo 	if (!fws)
244405491d2cSKalle Valo 		return;
24455cdb0ef6SPiotr Figiel 
2446a84a60ccSArend van Spriel 	if (fws->fws_wq)
2447a84a60ccSArend van Spriel 		destroy_workqueue(fws->fws_wq);
244805491d2cSKalle Valo 
244905491d2cSKalle Valo 	/* cleanup */
245005491d2cSKalle Valo 	brcmf_fws_lock(fws);
245105491d2cSKalle Valo 	brcmf_fws_cleanup(fws, -1);
245205491d2cSKalle Valo 	brcmf_fws_unlock(fws);
245305491d2cSKalle Valo 
245405491d2cSKalle Valo 	/* free top structure */
245505491d2cSKalle Valo 	kfree(fws);
245605491d2cSKalle Valo }
245705491d2cSKalle Valo 
brcmf_fws_debugfs_create(struct brcmf_pub * drvr)245834789d0cSArend Van Spriel void brcmf_fws_debugfs_create(struct brcmf_pub *drvr)
245934789d0cSArend Van Spriel {
246034789d0cSArend Van Spriel 	/* create debugfs file for statistics */
246134789d0cSArend Van Spriel 	brcmf_debugfs_add_entry(drvr, "fws_stats",
246234789d0cSArend Van Spriel 				brcmf_debugfs_fws_stats_read);
246334789d0cSArend Van Spriel }
246434789d0cSArend Van Spriel 
brcmf_fws_queue_skbs(struct brcmf_fws_info * fws)2465b073ac1fSRafał Miłecki bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws)
2466b073ac1fSRafał Miłecki {
2467b073ac1fSRafał Miłecki 	return !fws->avoid_queueing;
2468b073ac1fSRafał Miłecki }
2469b073ac1fSRafał Miłecki 
brcmf_fws_fc_active(struct brcmf_fws_info * fws)247005491d2cSKalle Valo bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
247105491d2cSKalle Valo {
247205491d2cSKalle Valo 	if (!fws->creditmap_received)
247305491d2cSKalle Valo 		return false;
247405491d2cSKalle Valo 
247505491d2cSKalle Valo 	return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
247605491d2cSKalle Valo }
247705491d2cSKalle Valo 
brcmf_fws_bustxcomplete(struct brcmf_fws_info * fws,struct sk_buff * skb,bool success)24782eee3db7SWataru Gohda void brcmf_fws_bustxcomplete(struct brcmf_fws_info *fws, struct sk_buff *skb,
24792eee3db7SWataru Gohda 			     bool success)
248005491d2cSKalle Valo {
248105491d2cSKalle Valo 	u32 hslot;
248205491d2cSKalle Valo 
248305491d2cSKalle Valo 	if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
248405491d2cSKalle Valo 		brcmu_pkt_buf_free_skb(skb);
248505491d2cSKalle Valo 		return;
248605491d2cSKalle Valo 	}
24872eee3db7SWataru Gohda 
24882eee3db7SWataru Gohda 	if (!success) {
248905491d2cSKalle Valo 		brcmf_fws_lock(fws);
249005491d2cSKalle Valo 		hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
24912eee3db7SWataru Gohda 		brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot,
24922eee3db7SWataru Gohda 				      0, 0, 1);
249305491d2cSKalle Valo 		brcmf_fws_unlock(fws);
249405491d2cSKalle Valo 	}
24952eee3db7SWataru Gohda }
249605491d2cSKalle Valo 
brcmf_fws_bus_blocked(struct brcmf_pub * drvr,bool flow_blocked)249705491d2cSKalle Valo void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
249805491d2cSKalle Valo {
2499acf8ac41SArend Van Spriel 	struct brcmf_fws_info *fws = drvr_to_fws(drvr);
250082bc9ab6SArend Van Spriel 	struct brcmf_if *ifp;
250182bc9ab6SArend Van Spriel 	int i;
250205491d2cSKalle Valo 
250382bc9ab6SArend Van Spriel 	if (fws->avoid_queueing) {
250482bc9ab6SArend Van Spriel 		for (i = 0; i < BRCMF_MAX_IFS; i++) {
250582bc9ab6SArend Van Spriel 			ifp = drvr->iflist[i];
250682bc9ab6SArend Van Spriel 			if (!ifp || !ifp->ndev)
250782bc9ab6SArend Van Spriel 				continue;
250882bc9ab6SArend Van Spriel 			brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW,
250982bc9ab6SArend Van Spriel 					     flow_blocked);
251082bc9ab6SArend Van Spriel 		}
251182bc9ab6SArend Van Spriel 	} else {
251205491d2cSKalle Valo 		fws->bus_flow_blocked = flow_blocked;
251305491d2cSKalle Valo 		if (!flow_blocked)
251405491d2cSKalle Valo 			brcmf_fws_schedule_deq(fws);
251505491d2cSKalle Valo 		else
251605491d2cSKalle Valo 			fws->stats.bus_flow_block++;
251705491d2cSKalle Valo 	}
251882bc9ab6SArend Van Spriel }
2519