162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2010 Broadcom Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/types.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/if_ether.h>
862306a36Sopenharmony_ci#include <linux/spinlock.h>
962306a36Sopenharmony_ci#include <linux/skbuff.h>
1062306a36Sopenharmony_ci#include <linux/netdevice.h>
1162306a36Sopenharmony_ci#include <linux/etherdevice.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/jiffies.h>
1462306a36Sopenharmony_ci#include <net/cfg80211.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <brcmu_utils.h>
1762306a36Sopenharmony_ci#include <brcmu_wifi.h>
1862306a36Sopenharmony_ci#include "core.h"
1962306a36Sopenharmony_ci#include "debug.h"
2062306a36Sopenharmony_ci#include "bus.h"
2162306a36Sopenharmony_ci#include "fwil.h"
2262306a36Sopenharmony_ci#include "fwil_types.h"
2362306a36Sopenharmony_ci#include "fweh.h"
2462306a36Sopenharmony_ci#include "fwsignal.h"
2562306a36Sopenharmony_ci#include "p2p.h"
2662306a36Sopenharmony_ci#include "cfg80211.h"
2762306a36Sopenharmony_ci#include "proto.h"
2862306a36Sopenharmony_ci#include "bcdc.h"
2962306a36Sopenharmony_ci#include "common.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/**
3262306a36Sopenharmony_ci * DOC: Firmware Signalling
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * Firmware can send signals to host and vice versa, which are passed in the
3562306a36Sopenharmony_ci * data packets using TLV based header. This signalling layer is on top of the
3662306a36Sopenharmony_ci * BDC bus protocol layer.
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * single definition for firmware-driver flow control tlv's.
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length).
4362306a36Sopenharmony_ci * A length value 0 indicates variable length tlv.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci#define BRCMF_FWS_TLV_DEFLIST \
4662306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \
4762306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \
4862306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \
4962306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \
5062306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \
5162306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(MACDESC_ADD,	6, 8) \
5262306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \
5362306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \
5462306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \
5562306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \
5662306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \
5762306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \
5862306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \
5962306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \
6062306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \
6162306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \
6262306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEF(FILLER, 255, 0)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci * enum brcmf_fws_tlv_type - definition of tlv identifiers.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_ci#define BRCMF_FWS_TLV_DEF(name, id, len) \
6862306a36Sopenharmony_ci	BRCMF_FWS_TYPE_ ## name =  id,
6962306a36Sopenharmony_cienum brcmf_fws_tlv_type {
7062306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEFLIST
7162306a36Sopenharmony_ci	BRCMF_FWS_TYPE_INVALID
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci#undef BRCMF_FWS_TLV_DEF
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*
7662306a36Sopenharmony_ci * enum brcmf_fws_tlv_len - definition of tlv lengths.
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_ci#define BRCMF_FWS_TLV_DEF(name, id, len) \
7962306a36Sopenharmony_ci	BRCMF_FWS_TYPE_ ## name ## _LEN = (len),
8062306a36Sopenharmony_cienum brcmf_fws_tlv_len {
8162306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEFLIST
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci#undef BRCMF_FWS_TLV_DEF
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* AMPDU rx reordering definitions */
8662306a36Sopenharmony_ci#define BRCMF_RXREORDER_FLOWID_OFFSET		0
8762306a36Sopenharmony_ci#define BRCMF_RXREORDER_MAXIDX_OFFSET		2
8862306a36Sopenharmony_ci#define BRCMF_RXREORDER_FLAGS_OFFSET		4
8962306a36Sopenharmony_ci#define BRCMF_RXREORDER_CURIDX_OFFSET		6
9062306a36Sopenharmony_ci#define BRCMF_RXREORDER_EXPIDX_OFFSET		8
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define BRCMF_RXREORDER_DEL_FLOW		0x01
9362306a36Sopenharmony_ci#define BRCMF_RXREORDER_FLUSH_ALL		0x02
9462306a36Sopenharmony_ci#define BRCMF_RXREORDER_CURIDX_VALID		0x04
9562306a36Sopenharmony_ci#define BRCMF_RXREORDER_EXPIDX_VALID		0x08
9662306a36Sopenharmony_ci#define BRCMF_RXREORDER_NEW_HOLE		0x10
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#ifdef DEBUG
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * brcmf_fws_tlv_names - array of tlv names.
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_ci#define BRCMF_FWS_TLV_DEF(name, id, len) \
10362306a36Sopenharmony_ci	{ id, #name },
10462306a36Sopenharmony_cistatic struct {
10562306a36Sopenharmony_ci	enum brcmf_fws_tlv_type id;
10662306a36Sopenharmony_ci	const char *name;
10762306a36Sopenharmony_ci} brcmf_fws_tlv_names[] = {
10862306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEFLIST
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci#undef BRCMF_FWS_TLV_DEF
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	int i;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++)
11862306a36Sopenharmony_ci		if (brcmf_fws_tlv_names[i].id == id)
11962306a36Sopenharmony_ci			return brcmf_fws_tlv_names[i].name;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return "INVALID";
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci#else
12462306a36Sopenharmony_cistatic const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	return "NODEBUG";
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci#endif /* DEBUG */
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/*
13162306a36Sopenharmony_ci * The PKTTAG tlv has additional bytes when firmware-signalling
13262306a36Sopenharmony_ci * mode has REUSESEQ flag set.
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_ci#define BRCMF_FWS_TYPE_SEQ_LEN				2
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/*
13762306a36Sopenharmony_ci * flags used to enable tlv signalling from firmware.
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_ci#define BRCMF_FWS_FLAGS_RSSI_SIGNALS			0x0001
14062306a36Sopenharmony_ci#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS			0x0002
14162306a36Sopenharmony_ci#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS		0x0004
14262306a36Sopenharmony_ci#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE	0x0008
14362306a36Sopenharmony_ci#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE	0x0010
14462306a36Sopenharmony_ci#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE		0x0020
14562306a36Sopenharmony_ci#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE		0x0040
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#define BRCMF_FWS_MAC_DESC_TABLE_SIZE			32
14862306a36Sopenharmony_ci#define BRCMF_FWS_MAC_DESC_ID_INVALID			0xff
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci#define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF			0
15162306a36Sopenharmony_ci#define BRCMF_FWS_HOSTIF_FLOWSTATE_ON			1
15262306a36Sopenharmony_ci#define BRCMF_FWS_FLOWCONTROL_HIWATER			128
15362306a36Sopenharmony_ci#define BRCMF_FWS_FLOWCONTROL_LOWATER			64
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci#define BRCMF_FWS_PSQ_PREC_COUNT		((BRCMF_FWS_FIFO_COUNT + 1) * 2)
15662306a36Sopenharmony_ci#define BRCMF_FWS_PSQ_LEN				256
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci#define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST			0x01
15962306a36Sopenharmony_ci#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED		0x02
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci#define BRCMF_FWS_RET_OK_NOSCHEDULE			0
16262306a36Sopenharmony_ci#define BRCMF_FWS_RET_OK_SCHEDULE			1
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci#define BRCMF_FWS_MODE_REUSESEQ_SHIFT			3	/* seq reuse */
16562306a36Sopenharmony_ci#define BRCMF_FWS_MODE_SET_REUSESEQ(x, val)	((x) = \
16662306a36Sopenharmony_ci		((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \
16762306a36Sopenharmony_ci		(((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT))
16862306a36Sopenharmony_ci#define BRCMF_FWS_MODE_GET_REUSESEQ(x)	\
16962306a36Sopenharmony_ci		(((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1)
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/**
17262306a36Sopenharmony_ci * enum brcmf_fws_skb_state - indicates processing state of skb.
17362306a36Sopenharmony_ci *
17462306a36Sopenharmony_ci * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver.
17562306a36Sopenharmony_ci * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue.
17662306a36Sopenharmony_ci * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware.
17762306a36Sopenharmony_ci * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info.
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_cienum brcmf_fws_skb_state {
18062306a36Sopenharmony_ci	BRCMF_FWS_SKBSTATE_NEW,
18162306a36Sopenharmony_ci	BRCMF_FWS_SKBSTATE_DELAYED,
18262306a36Sopenharmony_ci	BRCMF_FWS_SKBSTATE_SUPPRESSED,
18362306a36Sopenharmony_ci	BRCMF_FWS_SKBSTATE_TIM
18462306a36Sopenharmony_ci};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * struct brcmf_skbuff_cb - control buffer associated with skbuff.
18862306a36Sopenharmony_ci *
18962306a36Sopenharmony_ci * @bus_flags: 2 bytes reserved for bus specific parameters
19062306a36Sopenharmony_ci * @if_flags: holds interface index and packet related flags.
19162306a36Sopenharmony_ci * @htod: host to device packet identifier (used in PKTTAG tlv).
19262306a36Sopenharmony_ci * @htod_seq: this 16-bit is original seq number for every suppress packet.
19362306a36Sopenharmony_ci * @state: transmit state of the packet.
19462306a36Sopenharmony_ci * @mac: descriptor related to destination for this packet.
19562306a36Sopenharmony_ci *
19662306a36Sopenharmony_ci * This information is stored in control buffer struct sk_buff::cb, which
19762306a36Sopenharmony_ci * provides 48 bytes of storage so this structure should not exceed that.
19862306a36Sopenharmony_ci */
19962306a36Sopenharmony_cistruct brcmf_skbuff_cb {
20062306a36Sopenharmony_ci	u16 bus_flags;
20162306a36Sopenharmony_ci	u16 if_flags;
20262306a36Sopenharmony_ci	u32 htod;
20362306a36Sopenharmony_ci	u16 htod_seq;
20462306a36Sopenharmony_ci	enum brcmf_fws_skb_state state;
20562306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *mac;
20662306a36Sopenharmony_ci};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/*
20962306a36Sopenharmony_ci * macro casting skbuff control buffer to struct brcmf_skbuff_cb.
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_ci#define brcmf_skbcb(skb)	((struct brcmf_skbuff_cb *)((skb)->cb))
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/*
21462306a36Sopenharmony_ci * sk_buff control if flags
21562306a36Sopenharmony_ci *
21662306a36Sopenharmony_ci *	b[11]  - packet sent upon firmware request.
21762306a36Sopenharmony_ci *	b[10]  - packet only contains signalling data.
21862306a36Sopenharmony_ci *	b[9]   - packet is a tx packet.
21962306a36Sopenharmony_ci *	b[8]   - packet used requested credit
22062306a36Sopenharmony_ci *	b[7]   - interface in AP mode.
22162306a36Sopenharmony_ci *	b[3:0] - interface index.
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK	0x0800
22462306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT	11
22562306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK	0x0400
22662306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT	10
22762306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK        0x0200
22862306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT	9
22962306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK	0x0100
23062306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT	8
23162306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_IF_AP_MASK		0x0080
23262306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT		7
23362306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_INDEX_MASK		0x000f
23462306a36Sopenharmony_ci#define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT		0
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci#define brcmf_skb_if_flags_set_field(skb, field, value) \
23762306a36Sopenharmony_ci	brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \
23862306a36Sopenharmony_ci			BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
23962306a36Sopenharmony_ci			BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value))
24062306a36Sopenharmony_ci#define brcmf_skb_if_flags_get_field(skb, field) \
24162306a36Sopenharmony_ci	brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \
24262306a36Sopenharmony_ci			BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
24362306a36Sopenharmony_ci			BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT)
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/*
24662306a36Sopenharmony_ci * sk_buff control packet identifier
24762306a36Sopenharmony_ci *
24862306a36Sopenharmony_ci * 32-bit packet identifier used in PKTTAG tlv from host to dongle.
24962306a36Sopenharmony_ci *
25062306a36Sopenharmony_ci * - Generated at the host (e.g. dhd)
25162306a36Sopenharmony_ci * - Seen as a generic sequence number by firmware except for the flags field.
25262306a36Sopenharmony_ci *
25362306a36Sopenharmony_ci * Generation	: b[31]	=> generation number for this packet [host->fw]
25462306a36Sopenharmony_ci *			   OR, current generation number [fw->host]
25562306a36Sopenharmony_ci * Flags	: b[30:27] => command, status flags
25662306a36Sopenharmony_ci * FIFO-AC	: b[26:24] => AC-FIFO id
25762306a36Sopenharmony_ci * h-slot	: b[23:8] => hanger-slot
25862306a36Sopenharmony_ci * freerun	: b[7:0] => A free running counter
25962306a36Sopenharmony_ci */
26062306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_TAG_GENERATION_MASK		0x80000000
26162306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT		31
26262306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_TAG_FLAGS_MASK			0x78000000
26362306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT			27
26462306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_TAG_FIFO_MASK			0x07000000
26562306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT			24
26662306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_TAG_HSLOT_MASK			0x00ffff00
26762306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT			8
26862306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_TAG_FREERUN_MASK			0x000000ff
26962306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT		0
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci#define brcmf_skb_htod_tag_set_field(skb, field, value) \
27262306a36Sopenharmony_ci	brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \
27362306a36Sopenharmony_ci			BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
27462306a36Sopenharmony_ci			BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value))
27562306a36Sopenharmony_ci#define brcmf_skb_htod_tag_get_field(skb, field) \
27662306a36Sopenharmony_ci	brcmu_maskget32(brcmf_skbcb(skb)->htod, \
27762306a36Sopenharmony_ci			BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
27862306a36Sopenharmony_ci			BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT)
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK			0x2000
28162306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT			13
28262306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK			0x1000
28362306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT		12
28462306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_SEQ_NR_MASK			0x0fff
28562306a36Sopenharmony_ci#define BRCMF_SKB_HTOD_SEQ_NR_SHIFT			0
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci#define brcmf_skb_htod_seq_set_field(skb, field, value) \
28862306a36Sopenharmony_ci	brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \
28962306a36Sopenharmony_ci			BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
29062306a36Sopenharmony_ci			BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value))
29162306a36Sopenharmony_ci#define brcmf_skb_htod_seq_get_field(skb, field) \
29262306a36Sopenharmony_ci	brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \
29362306a36Sopenharmony_ci			BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
29462306a36Sopenharmony_ci			BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT)
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci#define BRCMF_FWS_TXSTAT_GENERATION_MASK	0x80000000
29762306a36Sopenharmony_ci#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT	31
29862306a36Sopenharmony_ci#define BRCMF_FWS_TXSTAT_FLAGS_MASK		0x78000000
29962306a36Sopenharmony_ci#define BRCMF_FWS_TXSTAT_FLAGS_SHIFT		27
30062306a36Sopenharmony_ci#define BRCMF_FWS_TXSTAT_FIFO_MASK		0x07000000
30162306a36Sopenharmony_ci#define BRCMF_FWS_TXSTAT_FIFO_SHIFT		24
30262306a36Sopenharmony_ci#define BRCMF_FWS_TXSTAT_HSLOT_MASK		0x00FFFF00
30362306a36Sopenharmony_ci#define BRCMF_FWS_TXSTAT_HSLOT_SHIFT		8
30462306a36Sopenharmony_ci#define BRCMF_FWS_TXSTAT_FREERUN_MASK		0x000000FF
30562306a36Sopenharmony_ci#define BRCMF_FWS_TXSTAT_FREERUN_SHIFT		0
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci#define brcmf_txstatus_get_field(txs, field) \
30862306a36Sopenharmony_ci	brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
30962306a36Sopenharmony_ci			BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci/* How long to defer borrowing in jiffies */
31262306a36Sopenharmony_ci#define BRCMF_FWS_BORROW_DEFER_PERIOD		(HZ / 10)
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/**
31662306a36Sopenharmony_ci * enum brcmf_fws_txstatus - txstatus flag values.
31762306a36Sopenharmony_ci *
31862306a36Sopenharmony_ci * @BRCMF_FWS_TXSTATUS_DISCARD:
31962306a36Sopenharmony_ci *	host is free to discard the packet.
32062306a36Sopenharmony_ci * @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS:
32162306a36Sopenharmony_ci *	802.11 core suppressed the packet.
32262306a36Sopenharmony_ci * @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS:
32362306a36Sopenharmony_ci *	firmware suppress the packet as device is already in PS mode.
32462306a36Sopenharmony_ci * @BRCMF_FWS_TXSTATUS_FW_TOSSED:
32562306a36Sopenharmony_ci *	firmware tossed the packet.
32662306a36Sopenharmony_ci * @BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK:
32762306a36Sopenharmony_ci *	firmware tossed the packet after retries.
32862306a36Sopenharmony_ci * @BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED:
32962306a36Sopenharmony_ci *	firmware wrongly reported suppressed previously, now fixing to acked.
33062306a36Sopenharmony_ci * @BRCMF_FWS_TXSTATUS_HOST_TOSSED:
33162306a36Sopenharmony_ci *	host tossed the packet.
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_cienum brcmf_fws_txstatus {
33462306a36Sopenharmony_ci	BRCMF_FWS_TXSTATUS_DISCARD,
33562306a36Sopenharmony_ci	BRCMF_FWS_TXSTATUS_CORE_SUPPRESS,
33662306a36Sopenharmony_ci	BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS,
33762306a36Sopenharmony_ci	BRCMF_FWS_TXSTATUS_FW_TOSSED,
33862306a36Sopenharmony_ci	BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK,
33962306a36Sopenharmony_ci	BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED,
34062306a36Sopenharmony_ci	BRCMF_FWS_TXSTATUS_HOST_TOSSED
34162306a36Sopenharmony_ci};
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cienum brcmf_fws_fcmode {
34462306a36Sopenharmony_ci	BRCMF_FWS_FCMODE_NONE,
34562306a36Sopenharmony_ci	BRCMF_FWS_FCMODE_IMPLIED_CREDIT,
34662306a36Sopenharmony_ci	BRCMF_FWS_FCMODE_EXPLICIT_CREDIT
34762306a36Sopenharmony_ci};
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cienum brcmf_fws_mac_desc_state {
35062306a36Sopenharmony_ci	BRCMF_FWS_STATE_OPEN = 1,
35162306a36Sopenharmony_ci	BRCMF_FWS_STATE_CLOSE
35262306a36Sopenharmony_ci};
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/**
35562306a36Sopenharmony_ci * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface
35662306a36Sopenharmony_ci *
35762306a36Sopenharmony_ci * @name: name of the descriptor.
35862306a36Sopenharmony_ci * @occupied: slot is in use.
35962306a36Sopenharmony_ci * @mac_handle: handle for mac entry determined by firmware.
36062306a36Sopenharmony_ci * @interface_id: interface index.
36162306a36Sopenharmony_ci * @state: current state.
36262306a36Sopenharmony_ci * @suppressed: mac entry is suppressed.
36362306a36Sopenharmony_ci * @generation: generation bit.
36462306a36Sopenharmony_ci * @ac_bitmap: ac queue bitmap.
36562306a36Sopenharmony_ci * @requested_credit: credits requested by firmware.
36662306a36Sopenharmony_ci * @requested_packet: packet requested by firmware.
36762306a36Sopenharmony_ci * @ea: ethernet address.
36862306a36Sopenharmony_ci * @seq: per-node free-running sequence.
36962306a36Sopenharmony_ci * @psq: power-save queue.
37062306a36Sopenharmony_ci * @transit_count: packet in transit to firmware.
37162306a36Sopenharmony_ci * @suppr_transit_count: suppressed packet in transit to firmware.
37262306a36Sopenharmony_ci * @send_tim_signal: if set tim signal will be sent.
37362306a36Sopenharmony_ci * @traffic_pending_bmp: traffic pending bitmap.
37462306a36Sopenharmony_ci * @traffic_lastreported_bmp: traffic last reported bitmap.
37562306a36Sopenharmony_ci */
37662306a36Sopenharmony_cistruct brcmf_fws_mac_descriptor {
37762306a36Sopenharmony_ci	char name[16];
37862306a36Sopenharmony_ci	u8 occupied;
37962306a36Sopenharmony_ci	u8 mac_handle;
38062306a36Sopenharmony_ci	u8 interface_id;
38162306a36Sopenharmony_ci	u8 state;
38262306a36Sopenharmony_ci	bool suppressed;
38362306a36Sopenharmony_ci	u8 generation;
38462306a36Sopenharmony_ci	u8 ac_bitmap;
38562306a36Sopenharmony_ci	u8 requested_credit;
38662306a36Sopenharmony_ci	u8 requested_packet;
38762306a36Sopenharmony_ci	u8 ea[ETH_ALEN];
38862306a36Sopenharmony_ci	u8 seq[BRCMF_FWS_FIFO_COUNT];
38962306a36Sopenharmony_ci	struct pktq psq;
39062306a36Sopenharmony_ci	int transit_count;
39162306a36Sopenharmony_ci	int suppr_transit_count;
39262306a36Sopenharmony_ci	bool send_tim_signal;
39362306a36Sopenharmony_ci	u8 traffic_pending_bmp;
39462306a36Sopenharmony_ci	u8 traffic_lastreported_bmp;
39562306a36Sopenharmony_ci};
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci#define BRCMF_FWS_HANGER_MAXITEMS	3072
39862306a36Sopenharmony_ci#define BRCMF_BORROW_RATIO			3
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/**
40162306a36Sopenharmony_ci * enum brcmf_fws_hanger_item_state - state of hanger item.
40262306a36Sopenharmony_ci *
40362306a36Sopenharmony_ci * @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use.
40462306a36Sopenharmony_ci * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use.
40562306a36Sopenharmony_ci * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed.
40662306a36Sopenharmony_ci */
40762306a36Sopenharmony_cienum brcmf_fws_hanger_item_state {
40862306a36Sopenharmony_ci	BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1,
40962306a36Sopenharmony_ci	BRCMF_FWS_HANGER_ITEM_STATE_INUSE,
41062306a36Sopenharmony_ci	BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED
41162306a36Sopenharmony_ci};
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/**
41562306a36Sopenharmony_ci * struct brcmf_fws_hanger_item - single entry for tx pending packet.
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * @state: entry is either free or occupied.
41862306a36Sopenharmony_ci * @pkt: packet itself.
41962306a36Sopenharmony_ci */
42062306a36Sopenharmony_cistruct brcmf_fws_hanger_item {
42162306a36Sopenharmony_ci	enum brcmf_fws_hanger_item_state state;
42262306a36Sopenharmony_ci	struct sk_buff *pkt;
42362306a36Sopenharmony_ci};
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci/**
42662306a36Sopenharmony_ci * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus.
42762306a36Sopenharmony_ci *
42862306a36Sopenharmony_ci * @pushed: packets pushed to await txstatus.
42962306a36Sopenharmony_ci * @popped: packets popped upon handling txstatus.
43062306a36Sopenharmony_ci * @failed_to_push: packets that could not be pushed.
43162306a36Sopenharmony_ci * @failed_to_pop: packets that could not be popped.
43262306a36Sopenharmony_ci * @failed_slotfind: packets for which failed to find an entry.
43362306a36Sopenharmony_ci * @slot_pos: last returned item index for a free entry.
43462306a36Sopenharmony_ci * @items: array of hanger items.
43562306a36Sopenharmony_ci */
43662306a36Sopenharmony_cistruct brcmf_fws_hanger {
43762306a36Sopenharmony_ci	u32 pushed;
43862306a36Sopenharmony_ci	u32 popped;
43962306a36Sopenharmony_ci	u32 failed_to_push;
44062306a36Sopenharmony_ci	u32 failed_to_pop;
44162306a36Sopenharmony_ci	u32 failed_slotfind;
44262306a36Sopenharmony_ci	u32 slot_pos;
44362306a36Sopenharmony_ci	struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS];
44462306a36Sopenharmony_ci};
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistruct brcmf_fws_macdesc_table {
44762306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE];
44862306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS];
44962306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor other;
45062306a36Sopenharmony_ci};
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistruct brcmf_fws_stats {
45362306a36Sopenharmony_ci	u32 tlv_parse_failed;
45462306a36Sopenharmony_ci	u32 tlv_invalid_type;
45562306a36Sopenharmony_ci	u32 header_only_pkt;
45662306a36Sopenharmony_ci	u32 header_pulls;
45762306a36Sopenharmony_ci	u32 pkt2bus;
45862306a36Sopenharmony_ci	u32 send_pkts[5];
45962306a36Sopenharmony_ci	u32 requested_sent[5];
46062306a36Sopenharmony_ci	u32 generic_error;
46162306a36Sopenharmony_ci	u32 mac_update_failed;
46262306a36Sopenharmony_ci	u32 mac_ps_update_failed;
46362306a36Sopenharmony_ci	u32 if_update_failed;
46462306a36Sopenharmony_ci	u32 packet_request_failed;
46562306a36Sopenharmony_ci	u32 credit_request_failed;
46662306a36Sopenharmony_ci	u32 rollback_success;
46762306a36Sopenharmony_ci	u32 rollback_failed;
46862306a36Sopenharmony_ci	u32 delayq_full_error;
46962306a36Sopenharmony_ci	u32 supprq_full_error;
47062306a36Sopenharmony_ci	u32 txs_indicate;
47162306a36Sopenharmony_ci	u32 txs_discard;
47262306a36Sopenharmony_ci	u32 txs_supp_core;
47362306a36Sopenharmony_ci	u32 txs_supp_ps;
47462306a36Sopenharmony_ci	u32 txs_tossed;
47562306a36Sopenharmony_ci	u32 txs_host_tossed;
47662306a36Sopenharmony_ci	u32 bus_flow_block;
47762306a36Sopenharmony_ci	u32 fws_flow_block;
47862306a36Sopenharmony_ci};
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistruct brcmf_fws_info {
48162306a36Sopenharmony_ci	struct brcmf_pub *drvr;
48262306a36Sopenharmony_ci	spinlock_t spinlock;
48362306a36Sopenharmony_ci	ulong flags;
48462306a36Sopenharmony_ci	struct brcmf_fws_stats stats;
48562306a36Sopenharmony_ci	struct brcmf_fws_hanger hanger;
48662306a36Sopenharmony_ci	enum brcmf_fws_fcmode fcmode;
48762306a36Sopenharmony_ci	bool fw_signals;
48862306a36Sopenharmony_ci	bool bcmc_credit_check;
48962306a36Sopenharmony_ci	struct brcmf_fws_macdesc_table desc;
49062306a36Sopenharmony_ci	struct workqueue_struct *fws_wq;
49162306a36Sopenharmony_ci	struct work_struct fws_dequeue_work;
49262306a36Sopenharmony_ci	u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
49362306a36Sopenharmony_ci	int fifo_credit[BRCMF_FWS_FIFO_COUNT];
49462306a36Sopenharmony_ci	int init_fifo_credit[BRCMF_FWS_FIFO_COUNT];
49562306a36Sopenharmony_ci	int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1]
49662306a36Sopenharmony_ci		[BRCMF_FWS_FIFO_AC_VO + 1];
49762306a36Sopenharmony_ci	int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
49862306a36Sopenharmony_ci	u32 fifo_credit_map;
49962306a36Sopenharmony_ci	u32 fifo_delay_map;
50062306a36Sopenharmony_ci	unsigned long borrow_defer_timestamp;
50162306a36Sopenharmony_ci	bool bus_flow_blocked;
50262306a36Sopenharmony_ci	bool creditmap_received;
50362306a36Sopenharmony_ci	u8 mode;
50462306a36Sopenharmony_ci	bool avoid_queueing;
50562306a36Sopenharmony_ci};
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci#define BRCMF_FWS_TLV_DEF(name, id, len) \
50862306a36Sopenharmony_ci	case BRCMF_FWS_TYPE_ ## name: \
50962306a36Sopenharmony_ci		return len;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci/**
51262306a36Sopenharmony_ci * brcmf_fws_get_tlv_len() - returns defined length for given tlv id.
51362306a36Sopenharmony_ci *
51462306a36Sopenharmony_ci * @fws: firmware-signalling information.
51562306a36Sopenharmony_ci * @id: identifier of the TLV.
51662306a36Sopenharmony_ci *
51762306a36Sopenharmony_ci * Return: the specified length for the given TLV; Otherwise -EINVAL.
51862306a36Sopenharmony_ci */
51962306a36Sopenharmony_cistatic int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
52062306a36Sopenharmony_ci				 enum brcmf_fws_tlv_type id)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	switch (id) {
52362306a36Sopenharmony_ci	BRCMF_FWS_TLV_DEFLIST
52462306a36Sopenharmony_ci	default:
52562306a36Sopenharmony_ci		fws->stats.tlv_invalid_type++;
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci	return -EINVAL;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci#undef BRCMF_FWS_TLV_DEF
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void brcmf_fws_lock(struct brcmf_fws_info *fws)
53362306a36Sopenharmony_ci		__acquires(&fws->spinlock)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	spin_lock_irqsave(&fws->spinlock, fws->flags);
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic void brcmf_fws_unlock(struct brcmf_fws_info *fws)
53962306a36Sopenharmony_ci		__releases(&fws->spinlock)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	spin_unlock_irqrestore(&fws->spinlock, fws->flags);
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
54762306a36Sopenharmony_ci	return ifidx == *(int *)arg;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	int i;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	memset(hanger, 0, sizeof(*hanger));
55562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
55662306a36Sopenharmony_ci		hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	u32 i;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	while (i != h->slot_pos) {
56662306a36Sopenharmony_ci		if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
56762306a36Sopenharmony_ci			h->slot_pos = i;
56862306a36Sopenharmony_ci			goto done;
56962306a36Sopenharmony_ci		}
57062306a36Sopenharmony_ci		i++;
57162306a36Sopenharmony_ci		if (i == BRCMF_FWS_HANGER_MAXITEMS)
57262306a36Sopenharmony_ci			i = 0;
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci	brcmf_err("all slots occupied\n");
57562306a36Sopenharmony_ci	h->failed_slotfind++;
57662306a36Sopenharmony_ci	i = BRCMF_FWS_HANGER_MAXITEMS;
57762306a36Sopenharmony_cidone:
57862306a36Sopenharmony_ci	return i;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
58262306a36Sopenharmony_ci				    struct sk_buff *pkt, u32 slot_id)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
58562306a36Sopenharmony_ci		return -ENOENT;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
58862306a36Sopenharmony_ci		brcmf_err("slot is not free\n");
58962306a36Sopenharmony_ci		h->failed_to_push++;
59062306a36Sopenharmony_ci		return -EINVAL;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE;
59462306a36Sopenharmony_ci	h->items[slot_id].pkt = pkt;
59562306a36Sopenharmony_ci	h->pushed++;
59662306a36Sopenharmony_ci	return 0;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
60062306a36Sopenharmony_ci					  u32 slot_id, struct sk_buff **pktout,
60162306a36Sopenharmony_ci					  bool remove_item)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
60462306a36Sopenharmony_ci		return -ENOENT;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
60762306a36Sopenharmony_ci		brcmf_err("entry not in use\n");
60862306a36Sopenharmony_ci		h->failed_to_pop++;
60962306a36Sopenharmony_ci		return -EINVAL;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	*pktout = h->items[slot_id].pkt;
61362306a36Sopenharmony_ci	if (remove_item) {
61462306a36Sopenharmony_ci		h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
61562306a36Sopenharmony_ci		h->items[slot_id].pkt = NULL;
61662306a36Sopenharmony_ci		h->popped++;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci	return 0;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q,
62262306a36Sopenharmony_ci				int ifidx)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct brcmf_fws_hanger_item *hi;
62562306a36Sopenharmony_ci	bool (*matchfn)(struct sk_buff *, void *) = NULL;
62662306a36Sopenharmony_ci	struct sk_buff *skb;
62762306a36Sopenharmony_ci	int prec;
62862306a36Sopenharmony_ci	u32 hslot;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (ifidx != -1)
63162306a36Sopenharmony_ci		matchfn = brcmf_fws_ifidx_match;
63262306a36Sopenharmony_ci	for (prec = 0; prec < q->num_prec; prec++) {
63362306a36Sopenharmony_ci		skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
63462306a36Sopenharmony_ci		while (skb) {
63562306a36Sopenharmony_ci			hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
63662306a36Sopenharmony_ci			hi = &fws->hanger.items[hslot];
63762306a36Sopenharmony_ci			WARN_ON(skb != hi->pkt);
63862306a36Sopenharmony_ci			hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
63962306a36Sopenharmony_ci			brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
64062306a36Sopenharmony_ci						true);
64162306a36Sopenharmony_ci			brcmu_pkt_buf_free_skb(skb);
64262306a36Sopenharmony_ci			skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
64362306a36Sopenharmony_ci		}
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
64862306a36Sopenharmony_ci					    u32 slot_id)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
65162306a36Sopenharmony_ci		return -ENOENT;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
65462306a36Sopenharmony_ci		brcmf_err("entry not in use\n");
65562306a36Sopenharmony_ci		return -EINVAL;
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
65962306a36Sopenharmony_ci	return 0;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
66362306a36Sopenharmony_ci				     bool (*fn)(struct sk_buff *, void *),
66462306a36Sopenharmony_ci				     int ifidx)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct brcmf_fws_hanger *h = &fws->hanger;
66762306a36Sopenharmony_ci	struct sk_buff *skb;
66862306a36Sopenharmony_ci	int i;
66962306a36Sopenharmony_ci	enum brcmf_fws_hanger_item_state s;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(h->items); i++) {
67262306a36Sopenharmony_ci		s = h->items[i].state;
67362306a36Sopenharmony_ci		if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
67462306a36Sopenharmony_ci		    s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
67562306a36Sopenharmony_ci			skb = h->items[i].pkt;
67662306a36Sopenharmony_ci			if (fn == NULL || fn(skb, &ifidx)) {
67762306a36Sopenharmony_ci				/* suppress packets freed from psq */
67862306a36Sopenharmony_ci				if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE)
67962306a36Sopenharmony_ci					brcmu_pkt_buf_free_skb(skb);
68062306a36Sopenharmony_ci				h->items[i].state =
68162306a36Sopenharmony_ci					BRCMF_FWS_HANGER_ITEM_STATE_FREE;
68262306a36Sopenharmony_ci			}
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws,
68862306a36Sopenharmony_ci				       struct brcmf_fws_mac_descriptor *desc)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	if (desc == &fws->desc.other)
69162306a36Sopenharmony_ci		strscpy(desc->name, "MAC-OTHER", sizeof(desc->name));
69262306a36Sopenharmony_ci	else if (desc->mac_handle)
69362306a36Sopenharmony_ci		scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d",
69462306a36Sopenharmony_ci			  desc->mac_handle, desc->interface_id);
69562306a36Sopenharmony_ci	else
69662306a36Sopenharmony_ci		scnprintf(desc->name, sizeof(desc->name), "MACIF:%d",
69762306a36Sopenharmony_ci			  desc->interface_id);
69862306a36Sopenharmony_ci}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistatic void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc,
70162306a36Sopenharmony_ci				   u8 *addr, u8 ifidx)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	brcmf_dbg(TRACE,
70462306a36Sopenharmony_ci		  "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx);
70562306a36Sopenharmony_ci	desc->occupied = 1;
70662306a36Sopenharmony_ci	desc->state = BRCMF_FWS_STATE_OPEN;
70762306a36Sopenharmony_ci	desc->requested_credit = 0;
70862306a36Sopenharmony_ci	desc->requested_packet = 0;
70962306a36Sopenharmony_ci	/* depending on use may need ifp->bsscfgidx instead */
71062306a36Sopenharmony_ci	desc->interface_id = ifidx;
71162306a36Sopenharmony_ci	desc->ac_bitmap = 0xff; /* update this when handling APSD */
71262306a36Sopenharmony_ci	if (addr)
71362306a36Sopenharmony_ci		memcpy(&desc->ea[0], addr, ETH_ALEN);
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic
71762306a36Sopenharmony_civoid brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	brcmf_dbg(TRACE,
72062306a36Sopenharmony_ci		  "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id);
72162306a36Sopenharmony_ci	desc->occupied = 0;
72262306a36Sopenharmony_ci	desc->state = BRCMF_FWS_STATE_CLOSE;
72362306a36Sopenharmony_ci	desc->requested_credit = 0;
72462306a36Sopenharmony_ci	desc->requested_packet = 0;
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic struct brcmf_fws_mac_descriptor *
72862306a36Sopenharmony_cibrcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry;
73162306a36Sopenharmony_ci	int i;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (ea == NULL)
73462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	entry = &fws->desc.nodes[0];
73762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) {
73862306a36Sopenharmony_ci		if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN))
73962306a36Sopenharmony_ci			return entry;
74062306a36Sopenharmony_ci		entry++;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	return ERR_PTR(-ENOENT);
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic struct brcmf_fws_mac_descriptor*
74762306a36Sopenharmony_cibrcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry;
75062306a36Sopenharmony_ci	bool multicast;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	multicast = is_multicast_ether_addr(da);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	/* Multicast destination, STA and P2P clients get the interface entry.
75562306a36Sopenharmony_ci	 * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
75662306a36Sopenharmony_ci	 * have their own entry.
75762306a36Sopenharmony_ci	 */
75862306a36Sopenharmony_ci	if (multicast && ifp->fws_desc) {
75962306a36Sopenharmony_ci		entry = ifp->fws_desc;
76062306a36Sopenharmony_ci		goto done;
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	entry = brcmf_fws_macdesc_lookup(fws, da);
76462306a36Sopenharmony_ci	if (IS_ERR(entry))
76562306a36Sopenharmony_ci		entry = ifp->fws_desc;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cidone:
76862306a36Sopenharmony_ci	return entry;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws,
77262306a36Sopenharmony_ci				     struct brcmf_fws_mac_descriptor *entry,
77362306a36Sopenharmony_ci				     int fifo)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *if_entry;
77662306a36Sopenharmony_ci	bool closed;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/* for unique destination entries the related interface
77962306a36Sopenharmony_ci	 * may be closed.
78062306a36Sopenharmony_ci	 */
78162306a36Sopenharmony_ci	if (entry->mac_handle) {
78262306a36Sopenharmony_ci		if_entry = &fws->desc.iface[entry->interface_id];
78362306a36Sopenharmony_ci		if (if_entry->state == BRCMF_FWS_STATE_CLOSE)
78462306a36Sopenharmony_ci			return true;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci	/* an entry is closed when the state is closed and
78762306a36Sopenharmony_ci	 * the firmware did not request anything.
78862306a36Sopenharmony_ci	 */
78962306a36Sopenharmony_ci	closed = entry->state == BRCMF_FWS_STATE_CLOSE &&
79062306a36Sopenharmony_ci		 !entry->requested_credit && !entry->requested_packet;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	/* Or firmware does not allow traffic for given fifo */
79362306a36Sopenharmony_ci	return closed || !(entry->ac_bitmap & BIT(fifo));
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,
79762306a36Sopenharmony_ci				      struct brcmf_fws_mac_descriptor *entry,
79862306a36Sopenharmony_ci				      int ifidx)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
80162306a36Sopenharmony_ci		brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
80262306a36Sopenharmony_ci		entry->occupied = !!(entry->psq.len);
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,
80762306a36Sopenharmony_ci				      bool (*fn)(struct sk_buff *, void *),
80862306a36Sopenharmony_ci				      int ifidx)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	struct brcmf_fws_hanger_item *hi;
81162306a36Sopenharmony_ci	struct pktq *txq;
81262306a36Sopenharmony_ci	struct sk_buff *skb;
81362306a36Sopenharmony_ci	int prec;
81462306a36Sopenharmony_ci	u32 hslot;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	txq = brcmf_bus_gettxq(fws->drvr->bus_if);
81762306a36Sopenharmony_ci	if (IS_ERR(txq)) {
81862306a36Sopenharmony_ci		brcmf_dbg(TRACE, "no txq to clean up\n");
81962306a36Sopenharmony_ci		return;
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	for (prec = 0; prec < txq->num_prec; prec++) {
82362306a36Sopenharmony_ci		skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
82462306a36Sopenharmony_ci		while (skb) {
82562306a36Sopenharmony_ci			hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
82662306a36Sopenharmony_ci			hi = &fws->hanger.items[hslot];
82762306a36Sopenharmony_ci			WARN_ON(skb != hi->pkt);
82862306a36Sopenharmony_ci			hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
82962306a36Sopenharmony_ci			brcmu_pkt_buf_free_skb(skb);
83062306a36Sopenharmony_ci			skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
83162306a36Sopenharmony_ci		}
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	int i;
83862306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *table;
83962306a36Sopenharmony_ci	bool (*matchfn)(struct sk_buff *, void *) = NULL;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (fws == NULL)
84262306a36Sopenharmony_ci		return;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	if (ifidx != -1)
84562306a36Sopenharmony_ci		matchfn = brcmf_fws_ifidx_match;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* cleanup individual nodes */
84862306a36Sopenharmony_ci	table = &fws->desc.nodes[0];
84962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)
85062306a36Sopenharmony_ci		brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx);
85362306a36Sopenharmony_ci	brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx);
85462306a36Sopenharmony_ci	brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
86062306a36Sopenharmony_ci	u8 *wlh;
86162306a36Sopenharmony_ci	u16 data_offset = 0;
86262306a36Sopenharmony_ci	u8 fillers;
86362306a36Sopenharmony_ci	__le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
86462306a36Sopenharmony_ci	__le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n",
86762306a36Sopenharmony_ci		  entry->name, brcmf_skb_if_flags_get_field(skb, INDEX),
86862306a36Sopenharmony_ci		  (le32_to_cpu(pkttag) >> 8) & 0xffff,
86962306a36Sopenharmony_ci		  brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq);
87062306a36Sopenharmony_ci	if (entry->send_tim_signal)
87162306a36Sopenharmony_ci		data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
87262306a36Sopenharmony_ci	if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
87362306a36Sopenharmony_ci		data_offset += BRCMF_FWS_TYPE_SEQ_LEN;
87462306a36Sopenharmony_ci	/* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
87562306a36Sopenharmony_ci	data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
87662306a36Sopenharmony_ci	fillers = round_up(data_offset, 4) - data_offset;
87762306a36Sopenharmony_ci	data_offset += fillers;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	skb_push(skb, data_offset);
88062306a36Sopenharmony_ci	wlh = skb->data;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
88362306a36Sopenharmony_ci	wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
88462306a36Sopenharmony_ci	memcpy(&wlh[2], &pkttag, sizeof(pkttag));
88562306a36Sopenharmony_ci	if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
88662306a36Sopenharmony_ci		wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN;
88762306a36Sopenharmony_ci		memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq,
88862306a36Sopenharmony_ci		       sizeof(pktseq));
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci	wlh += wlh[1] + 2;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (entry->send_tim_signal) {
89362306a36Sopenharmony_ci		entry->send_tim_signal = false;
89462306a36Sopenharmony_ci		wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
89562306a36Sopenharmony_ci		wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
89662306a36Sopenharmony_ci		wlh[2] = entry->mac_handle;
89762306a36Sopenharmony_ci		wlh[3] = entry->traffic_pending_bmp;
89862306a36Sopenharmony_ci		brcmf_dbg(TRACE, "adding TIM info: handle %d bmp 0x%X\n",
89962306a36Sopenharmony_ci			  entry->mac_handle, entry->traffic_pending_bmp);
90062306a36Sopenharmony_ci		wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
90162306a36Sopenharmony_ci		entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci	if (fillers)
90462306a36Sopenharmony_ci		memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	return (u8)(data_offset >> 2);
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
91062306a36Sopenharmony_ci				 struct brcmf_fws_mac_descriptor *entry,
91162306a36Sopenharmony_ci				 int fifo, bool send_immediately)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	struct sk_buff *skb;
91462306a36Sopenharmony_ci	struct brcmf_skbuff_cb *skcb;
91562306a36Sopenharmony_ci	s32 err;
91662306a36Sopenharmony_ci	u32 len;
91762306a36Sopenharmony_ci	u8 data_offset;
91862306a36Sopenharmony_ci	int ifidx;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	/* check delayedQ and suppressQ in one call using bitmap */
92162306a36Sopenharmony_ci	if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
92262306a36Sopenharmony_ci		entry->traffic_pending_bmp &= ~NBITVAL(fifo);
92362306a36Sopenharmony_ci	else
92462306a36Sopenharmony_ci		entry->traffic_pending_bmp |= NBITVAL(fifo);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	entry->send_tim_signal = false;
92762306a36Sopenharmony_ci	if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)
92862306a36Sopenharmony_ci		entry->send_tim_signal = true;
92962306a36Sopenharmony_ci	if (send_immediately && entry->send_tim_signal &&
93062306a36Sopenharmony_ci	    entry->state == BRCMF_FWS_STATE_CLOSE) {
93162306a36Sopenharmony_ci		/* create a dummy packet and sent that. The traffic          */
93262306a36Sopenharmony_ci		/* bitmap info will automatically be attached to that packet */
93362306a36Sopenharmony_ci		len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
93462306a36Sopenharmony_ci		      BRCMF_FWS_TYPE_SEQ_LEN +
93562306a36Sopenharmony_ci		      BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
93662306a36Sopenharmony_ci		      4 + fws->drvr->hdrlen;
93762306a36Sopenharmony_ci		skb = brcmu_pkt_buf_get_skb(len);
93862306a36Sopenharmony_ci		if (skb == NULL)
93962306a36Sopenharmony_ci			return false;
94062306a36Sopenharmony_ci		skb_pull(skb, len);
94162306a36Sopenharmony_ci		skcb = brcmf_skbcb(skb);
94262306a36Sopenharmony_ci		skcb->mac = entry;
94362306a36Sopenharmony_ci		skcb->state = BRCMF_FWS_SKBSTATE_TIM;
94462306a36Sopenharmony_ci		skcb->htod = 0;
94562306a36Sopenharmony_ci		skcb->htod_seq = 0;
94662306a36Sopenharmony_ci		data_offset = brcmf_fws_hdrpush(fws, skb);
94762306a36Sopenharmony_ci		ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
94862306a36Sopenharmony_ci		brcmf_fws_unlock(fws);
94962306a36Sopenharmony_ci		err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
95062306a36Sopenharmony_ci		brcmf_fws_lock(fws);
95162306a36Sopenharmony_ci		if (err)
95262306a36Sopenharmony_ci			brcmu_pkt_buf_free_skb(skb);
95362306a36Sopenharmony_ci		return true;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci	return false;
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_cistatic void
95962306a36Sopenharmony_cibrcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
96062306a36Sopenharmony_ci			     u8 if_id)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (WARN_ON(!ifp))
96562306a36Sopenharmony_ci		return;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
96862306a36Sopenharmony_ci	    pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER)
96962306a36Sopenharmony_ci		brcmf_txflowblock_if(ifp,
97062306a36Sopenharmony_ci				     BRCMF_NETIF_STOP_REASON_FWS_FC, false);
97162306a36Sopenharmony_ci	if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
97262306a36Sopenharmony_ci	    pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) {
97362306a36Sopenharmony_ci		fws->stats.fws_flow_block++;
97462306a36Sopenharmony_ci		brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci	return;
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cistatic int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	brcmf_dbg(CTL, "rssi %d\n", rssi);
98262306a36Sopenharmony_ci	return 0;
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_cistatic
98662306a36Sopenharmony_ciint brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry, *existing;
98962306a36Sopenharmony_ci	u8 mac_handle;
99062306a36Sopenharmony_ci	u8 ifidx;
99162306a36Sopenharmony_ci	u8 *addr;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	mac_handle = *data++;
99462306a36Sopenharmony_ci	ifidx = *data++;
99562306a36Sopenharmony_ci	addr = data;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	entry = &fws->desc.nodes[mac_handle & 0x1F];
99862306a36Sopenharmony_ci	if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
99962306a36Sopenharmony_ci		if (entry->occupied) {
100062306a36Sopenharmony_ci			brcmf_dbg(TRACE, "deleting %s mac %pM\n",
100162306a36Sopenharmony_ci				  entry->name, addr);
100262306a36Sopenharmony_ci			brcmf_fws_lock(fws);
100362306a36Sopenharmony_ci			brcmf_fws_macdesc_cleanup(fws, entry, -1);
100462306a36Sopenharmony_ci			brcmf_fws_macdesc_deinit(entry);
100562306a36Sopenharmony_ci			brcmf_fws_unlock(fws);
100662306a36Sopenharmony_ci		} else
100762306a36Sopenharmony_ci			fws->stats.mac_update_failed++;
100862306a36Sopenharmony_ci		return 0;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	existing = brcmf_fws_macdesc_lookup(fws, addr);
101262306a36Sopenharmony_ci	if (IS_ERR(existing)) {
101362306a36Sopenharmony_ci		if (!entry->occupied) {
101462306a36Sopenharmony_ci			brcmf_fws_lock(fws);
101562306a36Sopenharmony_ci			entry->mac_handle = mac_handle;
101662306a36Sopenharmony_ci			brcmf_fws_macdesc_init(entry, addr, ifidx);
101762306a36Sopenharmony_ci			brcmf_fws_macdesc_set_name(fws, entry);
101862306a36Sopenharmony_ci			brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
101962306a36Sopenharmony_ci					BRCMF_FWS_PSQ_LEN);
102062306a36Sopenharmony_ci			brcmf_fws_unlock(fws);
102162306a36Sopenharmony_ci			brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
102262306a36Sopenharmony_ci		} else {
102362306a36Sopenharmony_ci			fws->stats.mac_update_failed++;
102462306a36Sopenharmony_ci		}
102562306a36Sopenharmony_ci	} else {
102662306a36Sopenharmony_ci		if (entry != existing) {
102762306a36Sopenharmony_ci			brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
102862306a36Sopenharmony_ci			brcmf_fws_lock(fws);
102962306a36Sopenharmony_ci			memcpy(entry, existing,
103062306a36Sopenharmony_ci			       offsetof(struct brcmf_fws_mac_descriptor, psq));
103162306a36Sopenharmony_ci			entry->mac_handle = mac_handle;
103262306a36Sopenharmony_ci			brcmf_fws_macdesc_deinit(existing);
103362306a36Sopenharmony_ci			brcmf_fws_macdesc_set_name(fws, entry);
103462306a36Sopenharmony_ci			brcmf_fws_unlock(fws);
103562306a36Sopenharmony_ci			brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
103662306a36Sopenharmony_ci				  addr);
103762306a36Sopenharmony_ci		} else {
103862306a36Sopenharmony_ci			brcmf_dbg(TRACE, "use existing\n");
103962306a36Sopenharmony_ci			WARN_ON(entry->mac_handle != mac_handle);
104062306a36Sopenharmony_ci			/* TODO: what should we do here: continue, reinit, .. */
104162306a36Sopenharmony_ci		}
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci	return 0;
104462306a36Sopenharmony_ci}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_cistatic int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
104762306a36Sopenharmony_ci					    u8 type, u8 *data)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry;
105062306a36Sopenharmony_ci	u8 mac_handle;
105162306a36Sopenharmony_ci	int ret;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	mac_handle = data[0];
105462306a36Sopenharmony_ci	entry = &fws->desc.nodes[mac_handle & 0x1F];
105562306a36Sopenharmony_ci	if (!entry->occupied) {
105662306a36Sopenharmony_ci		fws->stats.mac_ps_update_failed++;
105762306a36Sopenharmony_ci		return -ESRCH;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci	brcmf_fws_lock(fws);
106062306a36Sopenharmony_ci	/* a state update should wipe old credits */
106162306a36Sopenharmony_ci	entry->requested_credit = 0;
106262306a36Sopenharmony_ci	entry->requested_packet = 0;
106362306a36Sopenharmony_ci	if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
106462306a36Sopenharmony_ci		entry->state = BRCMF_FWS_STATE_OPEN;
106562306a36Sopenharmony_ci		ret = BRCMF_FWS_RET_OK_SCHEDULE;
106662306a36Sopenharmony_ci	} else {
106762306a36Sopenharmony_ci		entry->state = BRCMF_FWS_STATE_CLOSE;
106862306a36Sopenharmony_ci		brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false);
106962306a36Sopenharmony_ci		brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false);
107062306a36Sopenharmony_ci		brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false);
107162306a36Sopenharmony_ci		brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
107262306a36Sopenharmony_ci		ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
107362306a36Sopenharmony_ci	}
107462306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
107562306a36Sopenharmony_ci	return ret;
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_cistatic int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
107962306a36Sopenharmony_ci					      u8 type, u8 *data)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry;
108262306a36Sopenharmony_ci	u8 ifidx;
108362306a36Sopenharmony_ci	int ret;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	ifidx = data[0];
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	if (ifidx >= BRCMF_MAX_IFS) {
108862306a36Sopenharmony_ci		ret = -ERANGE;
108962306a36Sopenharmony_ci		goto fail;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	entry = &fws->desc.iface[ifidx];
109362306a36Sopenharmony_ci	if (!entry->occupied) {
109462306a36Sopenharmony_ci		ret = -ESRCH;
109562306a36Sopenharmony_ci		goto fail;
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
109962306a36Sopenharmony_ci		  entry->name);
110062306a36Sopenharmony_ci	brcmf_fws_lock(fws);
110162306a36Sopenharmony_ci	switch (type) {
110262306a36Sopenharmony_ci	case BRCMF_FWS_TYPE_INTERFACE_OPEN:
110362306a36Sopenharmony_ci		entry->state = BRCMF_FWS_STATE_OPEN;
110462306a36Sopenharmony_ci		ret = BRCMF_FWS_RET_OK_SCHEDULE;
110562306a36Sopenharmony_ci		break;
110662306a36Sopenharmony_ci	case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
110762306a36Sopenharmony_ci		entry->state = BRCMF_FWS_STATE_CLOSE;
110862306a36Sopenharmony_ci		ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
110962306a36Sopenharmony_ci		break;
111062306a36Sopenharmony_ci	default:
111162306a36Sopenharmony_ci		ret = -EINVAL;
111262306a36Sopenharmony_ci		brcmf_fws_unlock(fws);
111362306a36Sopenharmony_ci		goto fail;
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
111662306a36Sopenharmony_ci	return ret;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_cifail:
111962306a36Sopenharmony_ci	fws->stats.if_update_failed++;
112062306a36Sopenharmony_ci	return ret;
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_cistatic int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
112462306a36Sopenharmony_ci				      u8 *data)
112562306a36Sopenharmony_ci{
112662306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	entry = &fws->desc.nodes[data[1] & 0x1F];
112962306a36Sopenharmony_ci	if (!entry->occupied) {
113062306a36Sopenharmony_ci		if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
113162306a36Sopenharmony_ci			fws->stats.credit_request_failed++;
113262306a36Sopenharmony_ci		else
113362306a36Sopenharmony_ci			fws->stats.packet_request_failed++;
113462306a36Sopenharmony_ci		return -ESRCH;
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
113862306a36Sopenharmony_ci		  brcmf_fws_get_tlv_name(type), type, entry->name,
113962306a36Sopenharmony_ci		  data[0], data[2]);
114062306a36Sopenharmony_ci	brcmf_fws_lock(fws);
114162306a36Sopenharmony_ci	if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
114262306a36Sopenharmony_ci		entry->requested_credit = data[0];
114362306a36Sopenharmony_ci	else
114462306a36Sopenharmony_ci		entry->requested_packet = data[0];
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	entry->ac_bitmap = data[2];
114762306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
114862306a36Sopenharmony_ci	return BRCMF_FWS_RET_OK_SCHEDULE;
114962306a36Sopenharmony_ci}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_cistatic void
115262306a36Sopenharmony_cibrcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry,
115362306a36Sopenharmony_ci				 struct sk_buff *skb)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	if (entry->requested_credit > 0) {
115662306a36Sopenharmony_ci		entry->requested_credit--;
115762306a36Sopenharmony_ci		brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
115862306a36Sopenharmony_ci		brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1);
115962306a36Sopenharmony_ci		if (entry->state != BRCMF_FWS_STATE_CLOSE)
116062306a36Sopenharmony_ci			brcmf_err("requested credit set while mac not closed!\n");
116162306a36Sopenharmony_ci	} else if (entry->requested_packet > 0) {
116262306a36Sopenharmony_ci		entry->requested_packet--;
116362306a36Sopenharmony_ci		brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
116462306a36Sopenharmony_ci		brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
116562306a36Sopenharmony_ci		if (entry->state != BRCMF_FWS_STATE_CLOSE)
116662306a36Sopenharmony_ci			brcmf_err("requested packet set while mac not closed!\n");
116762306a36Sopenharmony_ci	} else {
116862306a36Sopenharmony_ci		brcmf_skb_if_flags_set_field(skb, REQUESTED, 0);
116962306a36Sopenharmony_ci		brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) &&
117862306a36Sopenharmony_ci	    (entry->state == BRCMF_FWS_STATE_CLOSE))
117962306a36Sopenharmony_ci		entry->requested_credit++;
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
118362306a36Sopenharmony_ci				     u8 fifo, u8 credits)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	int lender_ac;
118662306a36Sopenharmony_ci	int *borrowed;
118762306a36Sopenharmony_ci	int *fifo_credit;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	if (!credits)
119062306a36Sopenharmony_ci		return;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	fws->fifo_credit_map |= 1 << fifo;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	if (fifo > BRCMF_FWS_FIFO_AC_BK &&
119562306a36Sopenharmony_ci	    fifo <= BRCMF_FWS_FIFO_AC_VO) {
119662306a36Sopenharmony_ci		for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
119762306a36Sopenharmony_ci		     lender_ac--) {
119862306a36Sopenharmony_ci			borrowed = &fws->credits_borrowed[fifo][lender_ac];
119962306a36Sopenharmony_ci			if (*borrowed) {
120062306a36Sopenharmony_ci				fws->fifo_credit_map |= (1 << lender_ac);
120162306a36Sopenharmony_ci				fifo_credit = &fws->fifo_credit[lender_ac];
120262306a36Sopenharmony_ci				if (*borrowed >= credits) {
120362306a36Sopenharmony_ci					*borrowed -= credits;
120462306a36Sopenharmony_ci					*fifo_credit += credits;
120562306a36Sopenharmony_ci					return;
120662306a36Sopenharmony_ci				} else {
120762306a36Sopenharmony_ci					credits -= *borrowed;
120862306a36Sopenharmony_ci					*fifo_credit += *borrowed;
120962306a36Sopenharmony_ci					*borrowed = 0;
121062306a36Sopenharmony_ci				}
121162306a36Sopenharmony_ci			}
121262306a36Sopenharmony_ci		}
121362306a36Sopenharmony_ci	}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	if (credits) {
121662306a36Sopenharmony_ci		fws->fifo_credit[fifo] += credits;
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	if (fws->fifo_credit[fifo] > fws->init_fifo_credit[fifo])
122062306a36Sopenharmony_ci		fws->fifo_credit[fifo] = fws->init_fifo_credit[fifo];
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_cistatic void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
122562306a36Sopenharmony_ci{
122662306a36Sopenharmony_ci	/* only schedule dequeue when there are credits for delayed traffic */
122762306a36Sopenharmony_ci	if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
122862306a36Sopenharmony_ci	    (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
122962306a36Sopenharmony_ci		queue_work(fws->fws_wq, &fws->fws_dequeue_work);
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_cistatic int brcmf_fws_enq(struct brcmf_fws_info *fws,
123362306a36Sopenharmony_ci			 enum brcmf_fws_skb_state state, int fifo,
123462306a36Sopenharmony_ci			 struct sk_buff *p)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	struct brcmf_pub *drvr = fws->drvr;
123762306a36Sopenharmony_ci	int prec = 2 * fifo;
123862306a36Sopenharmony_ci	u32 *qfull_stat = &fws->stats.delayq_full_error;
123962306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry;
124062306a36Sopenharmony_ci	struct pktq *pq;
124162306a36Sopenharmony_ci	struct sk_buff_head *queue;
124262306a36Sopenharmony_ci	struct sk_buff *p_head;
124362306a36Sopenharmony_ci	struct sk_buff *p_tail;
124462306a36Sopenharmony_ci	u32 fr_new;
124562306a36Sopenharmony_ci	u32 fr_compare;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	entry = brcmf_skbcb(p)->mac;
124862306a36Sopenharmony_ci	if (entry == NULL) {
124962306a36Sopenharmony_ci		bphy_err(drvr, "no mac descriptor found for skb %p\n", p);
125062306a36Sopenharmony_ci		return -ENOENT;
125162306a36Sopenharmony_ci	}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p);
125462306a36Sopenharmony_ci	if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
125562306a36Sopenharmony_ci		prec += 1;
125662306a36Sopenharmony_ci		qfull_stat = &fws->stats.supprq_full_error;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci		/* Fix out of order delivery of frames. Dont assume frame    */
125962306a36Sopenharmony_ci		/* can be inserted at the end, but look for correct position */
126062306a36Sopenharmony_ci		pq = &entry->psq;
126162306a36Sopenharmony_ci		if (pktq_full(pq) || pktq_pfull(pq, prec)) {
126262306a36Sopenharmony_ci			*qfull_stat += 1;
126362306a36Sopenharmony_ci			return -ENFILE;
126462306a36Sopenharmony_ci		}
126562306a36Sopenharmony_ci		queue = &pq->q[prec].skblist;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci		p_head = skb_peek(queue);
126862306a36Sopenharmony_ci		p_tail = skb_peek_tail(queue);
126962306a36Sopenharmony_ci		fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci		while (p_head != p_tail) {
127262306a36Sopenharmony_ci			fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
127362306a36Sopenharmony_ci								  FREERUN);
127462306a36Sopenharmony_ci			/* be sure to handle wrap of 256 */
127562306a36Sopenharmony_ci			if (((fr_new > fr_compare) &&
127662306a36Sopenharmony_ci			     ((fr_new - fr_compare) < 128)) ||
127762306a36Sopenharmony_ci			    ((fr_new < fr_compare) &&
127862306a36Sopenharmony_ci			     ((fr_compare - fr_new) > 128)))
127962306a36Sopenharmony_ci				break;
128062306a36Sopenharmony_ci			p_tail = skb_queue_prev(queue, p_tail);
128162306a36Sopenharmony_ci		}
128262306a36Sopenharmony_ci		/* Position found. Determine what to do */
128362306a36Sopenharmony_ci		if (p_tail == NULL) {
128462306a36Sopenharmony_ci			/* empty list */
128562306a36Sopenharmony_ci			__skb_queue_tail(queue, p);
128662306a36Sopenharmony_ci		} else {
128762306a36Sopenharmony_ci			fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
128862306a36Sopenharmony_ci								  FREERUN);
128962306a36Sopenharmony_ci			if (((fr_new > fr_compare) &&
129062306a36Sopenharmony_ci			     ((fr_new - fr_compare) < 128)) ||
129162306a36Sopenharmony_ci			    ((fr_new < fr_compare) &&
129262306a36Sopenharmony_ci			     ((fr_compare - fr_new) > 128))) {
129362306a36Sopenharmony_ci				/* After tail */
129462306a36Sopenharmony_ci				__skb_queue_after(queue, p_tail, p);
129562306a36Sopenharmony_ci			} else {
129662306a36Sopenharmony_ci				/* Before tail */
129762306a36Sopenharmony_ci				__skb_insert(p, p_tail->prev, p_tail, queue);
129862306a36Sopenharmony_ci			}
129962306a36Sopenharmony_ci		}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci		/* Complete the counters and statistics */
130262306a36Sopenharmony_ci		pq->len++;
130362306a36Sopenharmony_ci		if (pq->hi_prec < prec)
130462306a36Sopenharmony_ci			pq->hi_prec = (u8) prec;
130562306a36Sopenharmony_ci	} else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) {
130662306a36Sopenharmony_ci		*qfull_stat += 1;
130762306a36Sopenharmony_ci		return -ENFILE;
130862306a36Sopenharmony_ci	}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	/* increment total enqueued packet count */
131162306a36Sopenharmony_ci	fws->fifo_delay_map |= 1 << fifo;
131262306a36Sopenharmony_ci	fws->fifo_enqpkt[fifo]++;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	/* update the sk_buff state */
131562306a36Sopenharmony_ci	brcmf_skbcb(p)->state = state;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	/*
131862306a36Sopenharmony_ci	 * A packet has been pushed so update traffic
131962306a36Sopenharmony_ci	 * availability bitmap, if applicable
132062306a36Sopenharmony_ci	 */
132162306a36Sopenharmony_ci	brcmf_fws_tim_update(fws, entry, fifo, true);
132262306a36Sopenharmony_ci	brcmf_fws_flow_control_check(fws, &entry->psq,
132362306a36Sopenharmony_ci				     brcmf_skb_if_flags_get_field(p, INDEX));
132462306a36Sopenharmony_ci	return 0;
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cistatic struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *table;
133062306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry;
133162306a36Sopenharmony_ci	struct sk_buff *p;
133262306a36Sopenharmony_ci	int num_nodes;
133362306a36Sopenharmony_ci	int node_pos;
133462306a36Sopenharmony_ci	int prec_out;
133562306a36Sopenharmony_ci	int pmsk;
133662306a36Sopenharmony_ci	int i;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	table = (struct brcmf_fws_mac_descriptor *)&fws->desc;
133962306a36Sopenharmony_ci	num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor);
134062306a36Sopenharmony_ci	node_pos = fws->deq_node_pos[fifo];
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	for (i = 0; i < num_nodes; i++) {
134362306a36Sopenharmony_ci		entry = &table[(node_pos + i) % num_nodes];
134462306a36Sopenharmony_ci		if (!entry->occupied ||
134562306a36Sopenharmony_ci		    brcmf_fws_macdesc_closed(fws, entry, fifo))
134662306a36Sopenharmony_ci			continue;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci		if (entry->suppressed)
134962306a36Sopenharmony_ci			pmsk = 2;
135062306a36Sopenharmony_ci		else
135162306a36Sopenharmony_ci			pmsk = 3;
135262306a36Sopenharmony_ci		p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
135362306a36Sopenharmony_ci		if (p == NULL) {
135462306a36Sopenharmony_ci			if (entry->suppressed) {
135562306a36Sopenharmony_ci				if (entry->suppr_transit_count)
135662306a36Sopenharmony_ci					continue;
135762306a36Sopenharmony_ci				entry->suppressed = false;
135862306a36Sopenharmony_ci				p = brcmu_pktq_mdeq(&entry->psq,
135962306a36Sopenharmony_ci						    1 << (fifo * 2), &prec_out);
136062306a36Sopenharmony_ci			}
136162306a36Sopenharmony_ci		}
136262306a36Sopenharmony_ci		if  (p == NULL)
136362306a36Sopenharmony_ci			continue;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci		brcmf_fws_macdesc_use_req_credit(entry, p);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci		/* move dequeue position to ensure fair round-robin */
136862306a36Sopenharmony_ci		fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes;
136962306a36Sopenharmony_ci		brcmf_fws_flow_control_check(fws, &entry->psq,
137062306a36Sopenharmony_ci					     brcmf_skb_if_flags_get_field(p,
137162306a36Sopenharmony_ci									  INDEX)
137262306a36Sopenharmony_ci					     );
137362306a36Sopenharmony_ci		/*
137462306a36Sopenharmony_ci		 * A packet has been picked up, update traffic
137562306a36Sopenharmony_ci		 * availability bitmap, if applicable
137662306a36Sopenharmony_ci		 */
137762306a36Sopenharmony_ci		brcmf_fws_tim_update(fws, entry, fifo, false);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci		/*
138062306a36Sopenharmony_ci		 * decrement total enqueued fifo packets and
138162306a36Sopenharmony_ci		 * clear delay bitmap if done.
138262306a36Sopenharmony_ci		 */
138362306a36Sopenharmony_ci		fws->fifo_enqpkt[fifo]--;
138462306a36Sopenharmony_ci		if (fws->fifo_enqpkt[fifo] == 0)
138562306a36Sopenharmony_ci			fws->fifo_delay_map &= ~(1 << fifo);
138662306a36Sopenharmony_ci		goto done;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci	p = NULL;
138962306a36Sopenharmony_cidone:
139062306a36Sopenharmony_ci	brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);
139162306a36Sopenharmony_ci	return p;
139262306a36Sopenharmony_ci}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_cistatic int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
139562306a36Sopenharmony_ci					 struct sk_buff *skb,
139662306a36Sopenharmony_ci					 u32 genbit, u16 seq)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
139962306a36Sopenharmony_ci	u32 hslot;
140062306a36Sopenharmony_ci	int ret;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	/* this packet was suppressed */
140562306a36Sopenharmony_ci	if (!entry->suppressed) {
140662306a36Sopenharmony_ci		entry->suppressed = true;
140762306a36Sopenharmony_ci		entry->suppr_transit_count = entry->transit_count;
140862306a36Sopenharmony_ci		brcmf_dbg(DATA, "suppress %s: transit %d\n",
140962306a36Sopenharmony_ci			  entry->name, entry->transit_count);
141062306a36Sopenharmony_ci	}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	entry->generation = genbit;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
141562306a36Sopenharmony_ci	brcmf_skbcb(skb)->htod_seq = seq;
141662306a36Sopenharmony_ci	if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
141762306a36Sopenharmony_ci		brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
141862306a36Sopenharmony_ci		brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
141962306a36Sopenharmony_ci	} else {
142062306a36Sopenharmony_ci		brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
142162306a36Sopenharmony_ci	}
142262306a36Sopenharmony_ci	ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	if (ret != 0) {
142562306a36Sopenharmony_ci		/* suppress q is full drop this packet */
142662306a36Sopenharmony_ci		brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true);
142762306a36Sopenharmony_ci	} else {
142862306a36Sopenharmony_ci		/* Mark suppressed to avoid a double free during wlfc cleanup */
142962306a36Sopenharmony_ci		brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	return ret;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_cistatic int
143662306a36Sopenharmony_cibrcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
143762306a36Sopenharmony_ci		      u32 genbit, u16 seq, u8 compcnt)
143862306a36Sopenharmony_ci{
143962306a36Sopenharmony_ci	struct brcmf_pub *drvr = fws->drvr;
144062306a36Sopenharmony_ci	u32 fifo;
144162306a36Sopenharmony_ci	u8 cnt = 0;
144262306a36Sopenharmony_ci	int ret;
144362306a36Sopenharmony_ci	bool remove_from_hanger = true;
144462306a36Sopenharmony_ci	struct sk_buff *skb;
144562306a36Sopenharmony_ci	struct brcmf_skbuff_cb *skcb;
144662306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry = NULL;
144762306a36Sopenharmony_ci	struct brcmf_if *ifp;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	brcmf_dbg(DATA, "flags %d\n", flags);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
145262306a36Sopenharmony_ci		fws->stats.txs_discard += compcnt;
145362306a36Sopenharmony_ci	else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) {
145462306a36Sopenharmony_ci		fws->stats.txs_supp_core += compcnt;
145562306a36Sopenharmony_ci		remove_from_hanger = false;
145662306a36Sopenharmony_ci	} else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) {
145762306a36Sopenharmony_ci		fws->stats.txs_supp_ps += compcnt;
145862306a36Sopenharmony_ci		remove_from_hanger = false;
145962306a36Sopenharmony_ci	} else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)
146062306a36Sopenharmony_ci		fws->stats.txs_tossed += compcnt;
146162306a36Sopenharmony_ci	else if (flags == BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK)
146262306a36Sopenharmony_ci		fws->stats.txs_discard += compcnt;
146362306a36Sopenharmony_ci	else if (flags == BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED)
146462306a36Sopenharmony_ci		fws->stats.txs_discard += compcnt;
146562306a36Sopenharmony_ci	else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)
146662306a36Sopenharmony_ci		fws->stats.txs_host_tossed += compcnt;
146762306a36Sopenharmony_ci	else
146862306a36Sopenharmony_ci		bphy_err(drvr, "unexpected txstatus\n");
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	while (cnt < compcnt) {
147162306a36Sopenharmony_ci		ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
147262306a36Sopenharmony_ci					      remove_from_hanger);
147362306a36Sopenharmony_ci		if (ret != 0) {
147462306a36Sopenharmony_ci			bphy_err(drvr, "no packet in hanger slot: hslot=%d\n",
147562306a36Sopenharmony_ci				 hslot);
147662306a36Sopenharmony_ci			goto cont;
147762306a36Sopenharmony_ci		}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci		skcb = brcmf_skbcb(skb);
148062306a36Sopenharmony_ci		entry = skcb->mac;
148162306a36Sopenharmony_ci		if (WARN_ON(!entry)) {
148262306a36Sopenharmony_ci			brcmu_pkt_buf_free_skb(skb);
148362306a36Sopenharmony_ci			goto cont;
148462306a36Sopenharmony_ci		}
148562306a36Sopenharmony_ci		entry->transit_count--;
148662306a36Sopenharmony_ci		if (entry->suppressed && entry->suppr_transit_count)
148762306a36Sopenharmony_ci			entry->suppr_transit_count--;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci		brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name,
149062306a36Sopenharmony_ci			  flags, skcb->htod, seq);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci		/* pick up the implicit credit from this packet */
149362306a36Sopenharmony_ci		fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
149462306a36Sopenharmony_ci		if (fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT ||
149562306a36Sopenharmony_ci		    (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
149662306a36Sopenharmony_ci		    flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED) {
149762306a36Sopenharmony_ci			brcmf_fws_return_credits(fws, fifo, 1);
149862306a36Sopenharmony_ci			brcmf_fws_schedule_deq(fws);
149962306a36Sopenharmony_ci		}
150062306a36Sopenharmony_ci		brcmf_fws_macdesc_return_req_credit(skb);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci		ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
150362306a36Sopenharmony_ci		if (ret) {
150462306a36Sopenharmony_ci			brcmu_pkt_buf_free_skb(skb);
150562306a36Sopenharmony_ci			goto cont;
150662306a36Sopenharmony_ci		}
150762306a36Sopenharmony_ci		if (!remove_from_hanger)
150862306a36Sopenharmony_ci			ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
150962306a36Sopenharmony_ci							    genbit, seq);
151062306a36Sopenharmony_ci		if (remove_from_hanger || ret)
151162306a36Sopenharmony_ci			brcmf_txfinalize(ifp, skb, true);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cicont:
151462306a36Sopenharmony_ci		hslot = (hslot + 1) & (BRCMF_FWS_TXSTAT_HSLOT_MASK >>
151562306a36Sopenharmony_ci				       BRCMF_FWS_TXSTAT_HSLOT_SHIFT);
151662306a36Sopenharmony_ci		if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
151762306a36Sopenharmony_ci			seq = (seq + 1) & BRCMF_SKB_HTOD_SEQ_NR_MASK;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci		cnt++;
152062306a36Sopenharmony_ci	}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	return 0;
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_cistatic int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
152662306a36Sopenharmony_ci					     u8 *data)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	int i;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
153162306a36Sopenharmony_ci		brcmf_dbg(INFO, "ignored\n");
153262306a36Sopenharmony_ci		return BRCMF_FWS_RET_OK_NOSCHEDULE;
153362306a36Sopenharmony_ci	}
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	brcmf_dbg(DATA, "enter: data %pM\n", data);
153662306a36Sopenharmony_ci	brcmf_fws_lock(fws);
153762306a36Sopenharmony_ci	for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
153862306a36Sopenharmony_ci		brcmf_fws_return_credits(fws, i, data[i]);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
154162306a36Sopenharmony_ci		  fws->fifo_delay_map);
154262306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
154362306a36Sopenharmony_ci	return BRCMF_FWS_RET_OK_SCHEDULE;
154462306a36Sopenharmony_ci}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_cistatic int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 type,
154762306a36Sopenharmony_ci				       u8 *data)
154862306a36Sopenharmony_ci{
154962306a36Sopenharmony_ci	__le32 status_le;
155062306a36Sopenharmony_ci	__le16 seq_le;
155162306a36Sopenharmony_ci	u32 status;
155262306a36Sopenharmony_ci	u32 hslot;
155362306a36Sopenharmony_ci	u32 genbit;
155462306a36Sopenharmony_ci	u8 flags;
155562306a36Sopenharmony_ci	u16 seq;
155662306a36Sopenharmony_ci	u8 compcnt;
155762306a36Sopenharmony_ci	u8 compcnt_offset = BRCMF_FWS_TYPE_TXSTATUS_LEN;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	memcpy(&status_le, data, sizeof(status_le));
156062306a36Sopenharmony_ci	status = le32_to_cpu(status_le);
156162306a36Sopenharmony_ci	flags = brcmf_txstatus_get_field(status, FLAGS);
156262306a36Sopenharmony_ci	hslot = brcmf_txstatus_get_field(status, HSLOT);
156362306a36Sopenharmony_ci	genbit = brcmf_txstatus_get_field(status, GENERATION);
156462306a36Sopenharmony_ci	if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
156562306a36Sopenharmony_ci		memcpy(&seq_le, &data[BRCMF_FWS_TYPE_TXSTATUS_LEN],
156662306a36Sopenharmony_ci		       sizeof(seq_le));
156762306a36Sopenharmony_ci		seq = le16_to_cpu(seq_le);
156862306a36Sopenharmony_ci		compcnt_offset += BRCMF_FWS_TYPE_SEQ_LEN;
156962306a36Sopenharmony_ci	} else {
157062306a36Sopenharmony_ci		seq = 0;
157162306a36Sopenharmony_ci	}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	if (type == BRCMF_FWS_TYPE_COMP_TXSTATUS)
157462306a36Sopenharmony_ci		compcnt = data[compcnt_offset];
157562306a36Sopenharmony_ci	else
157662306a36Sopenharmony_ci		compcnt = 1;
157762306a36Sopenharmony_ci	fws->stats.txs_indicate += compcnt;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	brcmf_fws_lock(fws);
158062306a36Sopenharmony_ci	brcmf_fws_txs_process(fws, flags, hslot, genbit, seq, compcnt);
158162306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
158262306a36Sopenharmony_ci	return BRCMF_FWS_RET_OK_NOSCHEDULE;
158362306a36Sopenharmony_ci}
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_cistatic int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
158662306a36Sopenharmony_ci{
158762306a36Sopenharmony_ci	__le32 timestamp;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	memcpy(&timestamp, &data[2], sizeof(timestamp));
159062306a36Sopenharmony_ci	brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],
159162306a36Sopenharmony_ci		  le32_to_cpu(timestamp));
159262306a36Sopenharmony_ci	return 0;
159362306a36Sopenharmony_ci}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_cistatic int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
159662306a36Sopenharmony_ci				       const struct brcmf_event_msg *e,
159762306a36Sopenharmony_ci				       void *data)
159862306a36Sopenharmony_ci{
159962306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
160062306a36Sopenharmony_ci	struct brcmf_fws_info *fws = drvr_to_fws(drvr);
160162306a36Sopenharmony_ci	int i;
160262306a36Sopenharmony_ci	u8 *credits = data;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
160562306a36Sopenharmony_ci		bphy_err(drvr, "event payload too small (%d)\n", e->datalen);
160662306a36Sopenharmony_ci		return -EINVAL;
160762306a36Sopenharmony_ci	}
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	fws->creditmap_received = true;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
161262306a36Sopenharmony_ci	brcmf_fws_lock(fws);
161362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
161462306a36Sopenharmony_ci		fws->fifo_credit[i] += credits[i] - fws->init_fifo_credit[i];
161562306a36Sopenharmony_ci		fws->init_fifo_credit[i] = credits[i];
161662306a36Sopenharmony_ci		if (fws->fifo_credit[i] > 0)
161762306a36Sopenharmony_ci			fws->fifo_credit_map |= 1 << i;
161862306a36Sopenharmony_ci		else
161962306a36Sopenharmony_ci			fws->fifo_credit_map &= ~(1 << i);
162062306a36Sopenharmony_ci		WARN_ONCE(fws->fifo_credit[i] < 0,
162162306a36Sopenharmony_ci			  "fifo_credit[%d] is negative(%d)\n", i,
162262306a36Sopenharmony_ci			  fws->fifo_credit[i]);
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci	brcmf_fws_schedule_deq(fws);
162562306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
162662306a36Sopenharmony_ci	return 0;
162762306a36Sopenharmony_ci}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_cistatic int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
163062306a36Sopenharmony_ci						const struct brcmf_event_msg *e,
163162306a36Sopenharmony_ci						void *data)
163262306a36Sopenharmony_ci{
163362306a36Sopenharmony_ci	struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	if (fws) {
163662306a36Sopenharmony_ci		brcmf_fws_lock(fws);
163762306a36Sopenharmony_ci		fws->bcmc_credit_check = true;
163862306a36Sopenharmony_ci		brcmf_fws_unlock(fws);
163962306a36Sopenharmony_ci	}
164062306a36Sopenharmony_ci	return 0;
164162306a36Sopenharmony_ci}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cistatic void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
164462306a36Sopenharmony_ci					 u8 start, u8 end,
164562306a36Sopenharmony_ci					 struct sk_buff_head *skb_list)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	/* initialize return list */
164862306a36Sopenharmony_ci	__skb_queue_head_init(skb_list);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	if (rfi->pend_pkts == 0) {
165162306a36Sopenharmony_ci		brcmf_dbg(INFO, "no packets in reorder queue\n");
165262306a36Sopenharmony_ci		return;
165362306a36Sopenharmony_ci	}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	do {
165662306a36Sopenharmony_ci		if (rfi->pktslots[start]) {
165762306a36Sopenharmony_ci			__skb_queue_tail(skb_list, rfi->pktslots[start]);
165862306a36Sopenharmony_ci			rfi->pktslots[start] = NULL;
165962306a36Sopenharmony_ci		}
166062306a36Sopenharmony_ci		start++;
166162306a36Sopenharmony_ci		if (start > rfi->max_idx)
166262306a36Sopenharmony_ci			start = 0;
166362306a36Sopenharmony_ci	} while (start != end);
166462306a36Sopenharmony_ci	rfi->pend_pkts -= skb_queue_len(skb_list);
166562306a36Sopenharmony_ci}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_civoid brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt)
166862306a36Sopenharmony_ci{
166962306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
167062306a36Sopenharmony_ci	u8 *reorder_data;
167162306a36Sopenharmony_ci	u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
167262306a36Sopenharmony_ci	struct brcmf_ampdu_rx_reorder *rfi;
167362306a36Sopenharmony_ci	struct sk_buff_head reorder_list;
167462306a36Sopenharmony_ci	struct sk_buff *pnext;
167562306a36Sopenharmony_ci	u8 flags;
167662306a36Sopenharmony_ci	u32 buf_size;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	reorder_data = ((struct brcmf_skb_reorder_data *)pkt->cb)->reorder;
167962306a36Sopenharmony_ci	flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
168062306a36Sopenharmony_ci	flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	/* validate flags and flow id */
168362306a36Sopenharmony_ci	if (flags == 0xFF) {
168462306a36Sopenharmony_ci		bphy_err(drvr, "invalid flags...so ignore this packet\n");
168562306a36Sopenharmony_ci		brcmf_netif_rx(ifp, pkt);
168662306a36Sopenharmony_ci		return;
168762306a36Sopenharmony_ci	}
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	rfi = ifp->drvr->reorder_flows[flow_id];
169062306a36Sopenharmony_ci	if (flags & BRCMF_RXREORDER_DEL_FLOW) {
169162306a36Sopenharmony_ci		brcmf_dbg(INFO, "flow-%d: delete\n",
169262306a36Sopenharmony_ci			  flow_id);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci		if (rfi == NULL) {
169562306a36Sopenharmony_ci			brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
169662306a36Sopenharmony_ci				  flow_id);
169762306a36Sopenharmony_ci			brcmf_netif_rx(ifp, pkt);
169862306a36Sopenharmony_ci			return;
169962306a36Sopenharmony_ci		}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci		brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
170262306a36Sopenharmony_ci					     &reorder_list);
170362306a36Sopenharmony_ci		/* add the last packet */
170462306a36Sopenharmony_ci		__skb_queue_tail(&reorder_list, pkt);
170562306a36Sopenharmony_ci		kfree(rfi);
170662306a36Sopenharmony_ci		ifp->drvr->reorder_flows[flow_id] = NULL;
170762306a36Sopenharmony_ci		goto netif_rx;
170862306a36Sopenharmony_ci	}
170962306a36Sopenharmony_ci	/* from here on we need a flow reorder instance */
171062306a36Sopenharmony_ci	if (rfi == NULL) {
171162306a36Sopenharmony_ci		buf_size = sizeof(*rfi);
171262306a36Sopenharmony_ci		max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci		buf_size += (max_idx + 1) * sizeof(pkt);
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci		/* allocate space for flow reorder info */
171762306a36Sopenharmony_ci		brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
171862306a36Sopenharmony_ci			  flow_id, max_idx);
171962306a36Sopenharmony_ci		rfi = kzalloc(buf_size, GFP_ATOMIC);
172062306a36Sopenharmony_ci		if (rfi == NULL) {
172162306a36Sopenharmony_ci			bphy_err(drvr, "failed to alloc buffer\n");
172262306a36Sopenharmony_ci			brcmf_netif_rx(ifp, pkt);
172362306a36Sopenharmony_ci			return;
172462306a36Sopenharmony_ci		}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci		ifp->drvr->reorder_flows[flow_id] = rfi;
172762306a36Sopenharmony_ci		rfi->pktslots = (struct sk_buff **)(rfi + 1);
172862306a36Sopenharmony_ci		rfi->max_idx = max_idx;
172962306a36Sopenharmony_ci	}
173062306a36Sopenharmony_ci	if (flags & BRCMF_RXREORDER_NEW_HOLE)  {
173162306a36Sopenharmony_ci		if (rfi->pend_pkts) {
173262306a36Sopenharmony_ci			brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
173362306a36Sopenharmony_ci						     rfi->exp_idx,
173462306a36Sopenharmony_ci						     &reorder_list);
173562306a36Sopenharmony_ci			WARN_ON(rfi->pend_pkts);
173662306a36Sopenharmony_ci		} else {
173762306a36Sopenharmony_ci			__skb_queue_head_init(&reorder_list);
173862306a36Sopenharmony_ci		}
173962306a36Sopenharmony_ci		rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
174062306a36Sopenharmony_ci		rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
174162306a36Sopenharmony_ci		rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
174262306a36Sopenharmony_ci		rfi->pktslots[rfi->cur_idx] = pkt;
174362306a36Sopenharmony_ci		rfi->pend_pkts++;
174462306a36Sopenharmony_ci		brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
174562306a36Sopenharmony_ci			  flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
174662306a36Sopenharmony_ci	} else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
174762306a36Sopenharmony_ci		cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
174862306a36Sopenharmony_ci		exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci		if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
175162306a36Sopenharmony_ci			/* still in the current hole */
175262306a36Sopenharmony_ci			/* enqueue the current on the buffer chain */
175362306a36Sopenharmony_ci			if (rfi->pktslots[cur_idx] != NULL) {
175462306a36Sopenharmony_ci				brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
175562306a36Sopenharmony_ci				brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
175662306a36Sopenharmony_ci				rfi->pktslots[cur_idx] = NULL;
175762306a36Sopenharmony_ci			}
175862306a36Sopenharmony_ci			rfi->pktslots[cur_idx] = pkt;
175962306a36Sopenharmony_ci			rfi->pend_pkts++;
176062306a36Sopenharmony_ci			rfi->cur_idx = cur_idx;
176162306a36Sopenharmony_ci			brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
176262306a36Sopenharmony_ci				  flow_id, cur_idx, exp_idx, rfi->pend_pkts);
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci			/* can return now as there is no reorder
176562306a36Sopenharmony_ci			 * list to process.
176662306a36Sopenharmony_ci			 */
176762306a36Sopenharmony_ci			return;
176862306a36Sopenharmony_ci		}
176962306a36Sopenharmony_ci		if (rfi->exp_idx == cur_idx) {
177062306a36Sopenharmony_ci			if (rfi->pktslots[cur_idx] != NULL) {
177162306a36Sopenharmony_ci				brcmf_dbg(INFO, "error buffer pending..free it\n");
177262306a36Sopenharmony_ci				brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
177362306a36Sopenharmony_ci				rfi->pktslots[cur_idx] = NULL;
177462306a36Sopenharmony_ci			}
177562306a36Sopenharmony_ci			rfi->pktslots[cur_idx] = pkt;
177662306a36Sopenharmony_ci			rfi->pend_pkts++;
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci			/* got the expected one. flush from current to expected
177962306a36Sopenharmony_ci			 * and update expected
178062306a36Sopenharmony_ci			 */
178162306a36Sopenharmony_ci			brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
178262306a36Sopenharmony_ci				  flow_id, cur_idx, exp_idx, rfi->pend_pkts);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci			rfi->cur_idx = cur_idx;
178562306a36Sopenharmony_ci			rfi->exp_idx = exp_idx;
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci			brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
178862306a36Sopenharmony_ci						     &reorder_list);
178962306a36Sopenharmony_ci			brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
179062306a36Sopenharmony_ci				  flow_id, skb_queue_len(&reorder_list),
179162306a36Sopenharmony_ci				  rfi->pend_pkts);
179262306a36Sopenharmony_ci		} else {
179362306a36Sopenharmony_ci			u8 end_idx;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci			brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
179662306a36Sopenharmony_ci				  flow_id, flags, rfi->cur_idx, rfi->exp_idx,
179762306a36Sopenharmony_ci				  cur_idx, exp_idx);
179862306a36Sopenharmony_ci			if (flags & BRCMF_RXREORDER_FLUSH_ALL)
179962306a36Sopenharmony_ci				end_idx = rfi->exp_idx;
180062306a36Sopenharmony_ci			else
180162306a36Sopenharmony_ci				end_idx = exp_idx;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci			/* flush pkts first */
180462306a36Sopenharmony_ci			brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
180562306a36Sopenharmony_ci						     &reorder_list);
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci			if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
180862306a36Sopenharmony_ci				__skb_queue_tail(&reorder_list, pkt);
180962306a36Sopenharmony_ci			} else {
181062306a36Sopenharmony_ci				rfi->pktslots[cur_idx] = pkt;
181162306a36Sopenharmony_ci				rfi->pend_pkts++;
181262306a36Sopenharmony_ci			}
181362306a36Sopenharmony_ci			rfi->exp_idx = exp_idx;
181462306a36Sopenharmony_ci			rfi->cur_idx = cur_idx;
181562306a36Sopenharmony_ci		}
181662306a36Sopenharmony_ci	} else {
181762306a36Sopenharmony_ci		/* explicity window move updating the expected index */
181862306a36Sopenharmony_ci		exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci		brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
182162306a36Sopenharmony_ci			  flow_id, flags, rfi->exp_idx, exp_idx);
182262306a36Sopenharmony_ci		if (flags & BRCMF_RXREORDER_FLUSH_ALL)
182362306a36Sopenharmony_ci			end_idx =  rfi->exp_idx;
182462306a36Sopenharmony_ci		else
182562306a36Sopenharmony_ci			end_idx =  exp_idx;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci		brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
182862306a36Sopenharmony_ci					     &reorder_list);
182962306a36Sopenharmony_ci		__skb_queue_tail(&reorder_list, pkt);
183062306a36Sopenharmony_ci		/* set the new expected idx */
183162306a36Sopenharmony_ci		rfi->exp_idx = exp_idx;
183262306a36Sopenharmony_ci	}
183362306a36Sopenharmony_cinetif_rx:
183462306a36Sopenharmony_ci	skb_queue_walk_safe(&reorder_list, pkt, pnext) {
183562306a36Sopenharmony_ci		__skb_unlink(pkt, &reorder_list);
183662306a36Sopenharmony_ci		brcmf_netif_rx(ifp, pkt);
183762306a36Sopenharmony_ci	}
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_civoid brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
184162306a36Sopenharmony_ci{
184262306a36Sopenharmony_ci	struct brcmf_skb_reorder_data *rd;
184362306a36Sopenharmony_ci	struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
184462306a36Sopenharmony_ci	u8 *signal_data;
184562306a36Sopenharmony_ci	s16 data_len;
184662306a36Sopenharmony_ci	u8 type;
184762306a36Sopenharmony_ci	u8 len;
184862306a36Sopenharmony_ci	u8 *data;
184962306a36Sopenharmony_ci	s32 status;
185062306a36Sopenharmony_ci	s32 err;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
185362306a36Sopenharmony_ci		  ifp->ifidx, skb->len, siglen);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	WARN_ON(siglen > skb->len);
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	if (siglen > skb->len)
185862306a36Sopenharmony_ci		siglen = skb->len;
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	if (!siglen)
186162306a36Sopenharmony_ci		return;
186262306a36Sopenharmony_ci	/* if flow control disabled, skip to packet data and leave */
186362306a36Sopenharmony_ci	if ((!fws) || (!fws->fw_signals)) {
186462306a36Sopenharmony_ci		skb_pull(skb, siglen);
186562306a36Sopenharmony_ci		return;
186662306a36Sopenharmony_ci	}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	fws->stats.header_pulls++;
186962306a36Sopenharmony_ci	data_len = siglen;
187062306a36Sopenharmony_ci	signal_data = skb->data;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	status = BRCMF_FWS_RET_OK_NOSCHEDULE;
187362306a36Sopenharmony_ci	while (data_len > 0) {
187462306a36Sopenharmony_ci		/* extract tlv info */
187562306a36Sopenharmony_ci		type = signal_data[0];
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci		/* FILLER type is actually not a TLV, but
187862306a36Sopenharmony_ci		 * a single byte that can be skipped.
187962306a36Sopenharmony_ci		 */
188062306a36Sopenharmony_ci		if (type == BRCMF_FWS_TYPE_FILLER) {
188162306a36Sopenharmony_ci			signal_data += 1;
188262306a36Sopenharmony_ci			data_len -= 1;
188362306a36Sopenharmony_ci			continue;
188462306a36Sopenharmony_ci		}
188562306a36Sopenharmony_ci		len = signal_data[1];
188662306a36Sopenharmony_ci		data = signal_data + 2;
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci		brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n",
188962306a36Sopenharmony_ci			  brcmf_fws_get_tlv_name(type), type, len,
189062306a36Sopenharmony_ci			  brcmf_fws_get_tlv_len(fws, type));
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci		/* abort parsing when length invalid */
189362306a36Sopenharmony_ci		if (data_len < len + 2)
189462306a36Sopenharmony_ci			break;
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci		if (len < brcmf_fws_get_tlv_len(fws, type))
189762306a36Sopenharmony_ci			break;
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci		err = BRCMF_FWS_RET_OK_NOSCHEDULE;
190062306a36Sopenharmony_ci		switch (type) {
190162306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
190262306a36Sopenharmony_ci			rd = (struct brcmf_skb_reorder_data *)skb->cb;
190362306a36Sopenharmony_ci			rd->reorder = data;
190462306a36Sopenharmony_ci			break;
190562306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_MACDESC_ADD:
190662306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_MACDESC_DEL:
190762306a36Sopenharmony_ci			brcmf_fws_macdesc_indicate(fws, type, data);
190862306a36Sopenharmony_ci			break;
190962306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_MAC_OPEN:
191062306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_MAC_CLOSE:
191162306a36Sopenharmony_ci			err = brcmf_fws_macdesc_state_indicate(fws, type, data);
191262306a36Sopenharmony_ci			break;
191362306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_INTERFACE_OPEN:
191462306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
191562306a36Sopenharmony_ci			err = brcmf_fws_interface_state_indicate(fws, type,
191662306a36Sopenharmony_ci								 data);
191762306a36Sopenharmony_ci			break;
191862306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
191962306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
192062306a36Sopenharmony_ci			err = brcmf_fws_request_indicate(fws, type, data);
192162306a36Sopenharmony_ci			break;
192262306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_TXSTATUS:
192362306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_COMP_TXSTATUS:
192462306a36Sopenharmony_ci			brcmf_fws_txstatus_indicate(fws, type, data);
192562306a36Sopenharmony_ci			break;
192662306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
192762306a36Sopenharmony_ci			err = brcmf_fws_fifocreditback_indicate(fws, data);
192862306a36Sopenharmony_ci			break;
192962306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_RSSI:
193062306a36Sopenharmony_ci			brcmf_fws_rssi_indicate(fws, *data);
193162306a36Sopenharmony_ci			break;
193262306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_TRANS_ID:
193362306a36Sopenharmony_ci			brcmf_fws_dbg_seqnum_check(fws, data);
193462306a36Sopenharmony_ci			break;
193562306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_PKTTAG:
193662306a36Sopenharmony_ci		case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP:
193762306a36Sopenharmony_ci		default:
193862306a36Sopenharmony_ci			fws->stats.tlv_invalid_type++;
193962306a36Sopenharmony_ci			break;
194062306a36Sopenharmony_ci		}
194162306a36Sopenharmony_ci		if (err == BRCMF_FWS_RET_OK_SCHEDULE)
194262306a36Sopenharmony_ci			status = BRCMF_FWS_RET_OK_SCHEDULE;
194362306a36Sopenharmony_ci		signal_data += len + 2;
194462306a36Sopenharmony_ci		data_len -= len + 2;
194562306a36Sopenharmony_ci	}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	if (data_len != 0)
194862306a36Sopenharmony_ci		fws->stats.tlv_parse_failed++;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	if (status == BRCMF_FWS_RET_OK_SCHEDULE)
195162306a36Sopenharmony_ci		brcmf_fws_schedule_deq(fws);
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	/* signalling processing result does
195462306a36Sopenharmony_ci	 * not affect the actual ethernet packet.
195562306a36Sopenharmony_ci	 */
195662306a36Sopenharmony_ci	skb_pull(skb, siglen);
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	/* this may be a signal-only packet
195962306a36Sopenharmony_ci	 */
196062306a36Sopenharmony_ci	if (skb->len == 0)
196162306a36Sopenharmony_ci		fws->stats.header_only_pkt++;
196262306a36Sopenharmony_ci}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_cistatic u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
196562306a36Sopenharmony_ci				   struct sk_buff *p)
196662306a36Sopenharmony_ci{
196762306a36Sopenharmony_ci	struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
196862306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry = skcb->mac;
196962306a36Sopenharmony_ci	u8 flags;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED)
197262306a36Sopenharmony_ci		brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);
197362306a36Sopenharmony_ci	flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
197462306a36Sopenharmony_ci	if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
197562306a36Sopenharmony_ci		/*
197662306a36Sopenharmony_ci		 * Indicate that this packet is being sent in response to an
197762306a36Sopenharmony_ci		 * explicit request from the firmware side.
197862306a36Sopenharmony_ci		 */
197962306a36Sopenharmony_ci		flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
198062306a36Sopenharmony_ci	}
198162306a36Sopenharmony_ci	brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
198262306a36Sopenharmony_ci	return brcmf_fws_hdrpush(fws, p);
198362306a36Sopenharmony_ci}
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_cistatic void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
198662306a36Sopenharmony_ci				   struct sk_buff *skb, int fifo)
198762306a36Sopenharmony_ci{
198862306a36Sopenharmony_ci	struct brcmf_pub *drvr = fws->drvr;
198962306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry;
199062306a36Sopenharmony_ci	struct sk_buff *pktout;
199162306a36Sopenharmony_ci	int qidx, hslot;
199262306a36Sopenharmony_ci	int rc = 0;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	entry = brcmf_skbcb(skb)->mac;
199562306a36Sopenharmony_ci	if (entry->occupied) {
199662306a36Sopenharmony_ci		qidx = 2 * fifo;
199762306a36Sopenharmony_ci		if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
199862306a36Sopenharmony_ci			qidx++;
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci		pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb);
200162306a36Sopenharmony_ci		if (pktout == NULL) {
200262306a36Sopenharmony_ci			bphy_err(drvr, "%s queue %d full\n", entry->name, qidx);
200362306a36Sopenharmony_ci			rc = -ENOSPC;
200462306a36Sopenharmony_ci		}
200562306a36Sopenharmony_ci	} else {
200662306a36Sopenharmony_ci		bphy_err(drvr, "%s entry removed\n", entry->name);
200762306a36Sopenharmony_ci		rc = -ENOENT;
200862306a36Sopenharmony_ci	}
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	if (rc) {
201162306a36Sopenharmony_ci		fws->stats.rollback_failed++;
201262306a36Sopenharmony_ci		hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
201362306a36Sopenharmony_ci		brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
201462306a36Sopenharmony_ci				      hslot, 0, 0, 1);
201562306a36Sopenharmony_ci	} else {
201662306a36Sopenharmony_ci		fws->stats.rollback_success++;
201762306a36Sopenharmony_ci		brcmf_fws_return_credits(fws, fifo, 1);
201862306a36Sopenharmony_ci		brcmf_fws_macdesc_return_req_credit(skb);
201962306a36Sopenharmony_ci	}
202062306a36Sopenharmony_ci}
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_cistatic int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws,
202362306a36Sopenharmony_ci				   int highest_lender_ac, int borrower_ac,
202462306a36Sopenharmony_ci				   bool borrow_all)
202562306a36Sopenharmony_ci{
202662306a36Sopenharmony_ci	int lender_ac, borrow_limit = 0;
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci		if (!borrow_all)
203162306a36Sopenharmony_ci			borrow_limit =
203262306a36Sopenharmony_ci			  fws->init_fifo_credit[lender_ac] / BRCMF_BORROW_RATIO;
203362306a36Sopenharmony_ci		else
203462306a36Sopenharmony_ci			borrow_limit = 0;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci		if (fws->fifo_credit[lender_ac] > borrow_limit) {
203762306a36Sopenharmony_ci			fws->credits_borrowed[borrower_ac][lender_ac]++;
203862306a36Sopenharmony_ci			fws->fifo_credit[lender_ac]--;
203962306a36Sopenharmony_ci			if (fws->fifo_credit[lender_ac] == 0)
204062306a36Sopenharmony_ci				fws->fifo_credit_map &= ~(1 << lender_ac);
204162306a36Sopenharmony_ci			fws->fifo_credit_map |= (1 << borrower_ac);
204262306a36Sopenharmony_ci			brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac);
204362306a36Sopenharmony_ci			return 0;
204462306a36Sopenharmony_ci		}
204562306a36Sopenharmony_ci	}
204662306a36Sopenharmony_ci	fws->fifo_credit_map &= ~(1 << borrower_ac);
204762306a36Sopenharmony_ci	return -ENAVAIL;
204862306a36Sopenharmony_ci}
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_cistatic int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
205162306a36Sopenharmony_ci				struct sk_buff *skb)
205262306a36Sopenharmony_ci{
205362306a36Sopenharmony_ci	struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
205462306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry;
205562306a36Sopenharmony_ci	int rc;
205662306a36Sopenharmony_ci	u8 ifidx;
205762306a36Sopenharmony_ci	u8 data_offset;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	entry = skcb->mac;
206062306a36Sopenharmony_ci	if (IS_ERR(entry))
206162306a36Sopenharmony_ci		return PTR_ERR(entry);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	data_offset = brcmf_fws_precommit_skb(fws, fifo, skb);
206462306a36Sopenharmony_ci	entry->transit_count++;
206562306a36Sopenharmony_ci	if (entry->suppressed)
206662306a36Sopenharmony_ci		entry->suppr_transit_count++;
206762306a36Sopenharmony_ci	ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
206862306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
206962306a36Sopenharmony_ci	rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
207062306a36Sopenharmony_ci	brcmf_fws_lock(fws);
207162306a36Sopenharmony_ci	brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
207262306a36Sopenharmony_ci		  skcb->if_flags, skcb->htod, rc);
207362306a36Sopenharmony_ci	if (rc < 0) {
207462306a36Sopenharmony_ci		entry->transit_count--;
207562306a36Sopenharmony_ci		if (entry->suppressed)
207662306a36Sopenharmony_ci			entry->suppr_transit_count--;
207762306a36Sopenharmony_ci		(void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
207862306a36Sopenharmony_ci		goto rollback;
207962306a36Sopenharmony_ci	}
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	fws->stats.pkt2bus++;
208262306a36Sopenharmony_ci	fws->stats.send_pkts[fifo]++;
208362306a36Sopenharmony_ci	if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
208462306a36Sopenharmony_ci		fws->stats.requested_sent[fifo]++;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	return rc;
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_cirollback:
208962306a36Sopenharmony_ci	brcmf_fws_rollback_toq(fws, skb, fifo);
209062306a36Sopenharmony_ci	return rc;
209162306a36Sopenharmony_ci}
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_cistatic int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,
209462306a36Sopenharmony_ci				  int fifo)
209562306a36Sopenharmony_ci{
209662306a36Sopenharmony_ci	struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
209762306a36Sopenharmony_ci	int rc, hslot;
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	skcb->htod = 0;
210062306a36Sopenharmony_ci	skcb->htod_seq = 0;
210162306a36Sopenharmony_ci	hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
210262306a36Sopenharmony_ci	brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
210362306a36Sopenharmony_ci	brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);
210462306a36Sopenharmony_ci	brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
210562306a36Sopenharmony_ci	rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
210662306a36Sopenharmony_ci	if (!rc)
210762306a36Sopenharmony_ci		skcb->mac->seq[fifo]++;
210862306a36Sopenharmony_ci	else
210962306a36Sopenharmony_ci		fws->stats.generic_error++;
211062306a36Sopenharmony_ci	return rc;
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ciint brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
211462306a36Sopenharmony_ci{
211562306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
211662306a36Sopenharmony_ci	struct brcmf_fws_info *fws = drvr_to_fws(drvr);
211762306a36Sopenharmony_ci	struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
211862306a36Sopenharmony_ci	struct ethhdr *eh = (struct ethhdr *)(skb->data);
211962306a36Sopenharmony_ci	int fifo = BRCMF_FWS_FIFO_BCMC;
212062306a36Sopenharmony_ci	bool multicast = is_multicast_ether_addr(eh->h_dest);
212162306a36Sopenharmony_ci	int rc = 0;
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	/* set control buffer information */
212662306a36Sopenharmony_ci	skcb->if_flags = 0;
212762306a36Sopenharmony_ci	skcb->state = BRCMF_FWS_SKBSTATE_NEW;
212862306a36Sopenharmony_ci	brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	/* mapping from 802.1d priority to firmware fifo index */
213162306a36Sopenharmony_ci	if (!multicast)
213262306a36Sopenharmony_ci		fifo = brcmf_map_prio_to_aci(drvr->config, skb->priority);
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	brcmf_fws_lock(fws);
213562306a36Sopenharmony_ci	if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
213662306a36Sopenharmony_ci		fws->borrow_defer_timestamp = jiffies +
213762306a36Sopenharmony_ci					      BRCMF_FWS_BORROW_DEFER_PERIOD;
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);
214062306a36Sopenharmony_ci	brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,
214162306a36Sopenharmony_ci		  eh->h_dest, multicast, fifo);
214262306a36Sopenharmony_ci	if (!brcmf_fws_assign_htod(fws, skb, fifo)) {
214362306a36Sopenharmony_ci		brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
214462306a36Sopenharmony_ci		brcmf_fws_schedule_deq(fws);
214562306a36Sopenharmony_ci	} else {
214662306a36Sopenharmony_ci		bphy_err(drvr, "no hanger slot available\n");
214762306a36Sopenharmony_ci		rc = -ENOMEM;
214862306a36Sopenharmony_ci	}
214962306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	return rc;
215262306a36Sopenharmony_ci}
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_civoid brcmf_fws_reset_interface(struct brcmf_if *ifp)
215562306a36Sopenharmony_ci{
215662306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
215962306a36Sopenharmony_ci	if (!entry)
216062306a36Sopenharmony_ci		return;
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
216362306a36Sopenharmony_ci}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_civoid brcmf_fws_add_interface(struct brcmf_if *ifp)
216662306a36Sopenharmony_ci{
216762306a36Sopenharmony_ci	struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
216862306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry;
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	if (!ifp->ndev || !brcmf_fws_queue_skbs(fws))
217162306a36Sopenharmony_ci		return;
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	entry = &fws->desc.iface[ifp->ifidx];
217462306a36Sopenharmony_ci	ifp->fws_desc = entry;
217562306a36Sopenharmony_ci	brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
217662306a36Sopenharmony_ci	brcmf_fws_macdesc_set_name(fws, entry);
217762306a36Sopenharmony_ci	brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
217862306a36Sopenharmony_ci			BRCMF_FWS_PSQ_LEN);
217962306a36Sopenharmony_ci	brcmf_dbg(TRACE, "added %s\n", entry->name);
218062306a36Sopenharmony_ci}
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_civoid brcmf_fws_del_interface(struct brcmf_if *ifp)
218362306a36Sopenharmony_ci{
218462306a36Sopenharmony_ci	struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
218562306a36Sopenharmony_ci	struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	if (!entry)
218862306a36Sopenharmony_ci		return;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	brcmf_fws_lock(fws);
219162306a36Sopenharmony_ci	ifp->fws_desc = NULL;
219262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "deleting %s\n", entry->name);
219362306a36Sopenharmony_ci	brcmf_fws_macdesc_cleanup(fws, &fws->desc.iface[ifp->ifidx],
219462306a36Sopenharmony_ci				  ifp->ifidx);
219562306a36Sopenharmony_ci	brcmf_fws_macdesc_deinit(entry);
219662306a36Sopenharmony_ci	brcmf_fws_cleanup(fws, ifp->ifidx);
219762306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
219862306a36Sopenharmony_ci}
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_cistatic void brcmf_fws_dequeue_worker(struct work_struct *worker)
220162306a36Sopenharmony_ci{
220262306a36Sopenharmony_ci	struct brcmf_fws_info *fws;
220362306a36Sopenharmony_ci	struct brcmf_pub *drvr;
220462306a36Sopenharmony_ci	struct sk_buff *skb;
220562306a36Sopenharmony_ci	int fifo;
220662306a36Sopenharmony_ci	u32 hslot;
220762306a36Sopenharmony_ci	u32 ifidx;
220862306a36Sopenharmony_ci	int ret;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
221162306a36Sopenharmony_ci	drvr = fws->drvr;
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	brcmf_fws_lock(fws);
221462306a36Sopenharmony_ci	for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
221562306a36Sopenharmony_ci	     fifo--) {
221662306a36Sopenharmony_ci		if (!brcmf_fws_fc_active(fws)) {
221762306a36Sopenharmony_ci			while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
221862306a36Sopenharmony_ci				hslot = brcmf_skb_htod_tag_get_field(skb,
221962306a36Sopenharmony_ci								     HSLOT);
222062306a36Sopenharmony_ci				brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
222162306a36Sopenharmony_ci							&skb, true);
222262306a36Sopenharmony_ci				ifidx = brcmf_skb_if_flags_get_field(skb,
222362306a36Sopenharmony_ci								     INDEX);
222462306a36Sopenharmony_ci				/* Use proto layer to send data frame */
222562306a36Sopenharmony_ci				brcmf_fws_unlock(fws);
222662306a36Sopenharmony_ci				ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
222762306a36Sopenharmony_ci				brcmf_fws_lock(fws);
222862306a36Sopenharmony_ci				if (ret < 0)
222962306a36Sopenharmony_ci					brcmf_txfinalize(brcmf_get_ifp(drvr,
223062306a36Sopenharmony_ci								       ifidx),
223162306a36Sopenharmony_ci							 skb, false);
223262306a36Sopenharmony_ci				if (fws->bus_flow_blocked)
223362306a36Sopenharmony_ci					break;
223462306a36Sopenharmony_ci			}
223562306a36Sopenharmony_ci			continue;
223662306a36Sopenharmony_ci		}
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci		while ((fws->fifo_credit[fifo]) ||
223962306a36Sopenharmony_ci		       ((!fws->bcmc_credit_check) &&
224062306a36Sopenharmony_ci				(fifo == BRCMF_FWS_FIFO_BCMC))) {
224162306a36Sopenharmony_ci			skb = brcmf_fws_deq(fws, fifo);
224262306a36Sopenharmony_ci			if (!skb)
224362306a36Sopenharmony_ci				break;
224462306a36Sopenharmony_ci			fws->fifo_credit[fifo]--;
224562306a36Sopenharmony_ci			if (brcmf_fws_commit_skb(fws, fifo, skb))
224662306a36Sopenharmony_ci				break;
224762306a36Sopenharmony_ci			if (fws->bus_flow_blocked)
224862306a36Sopenharmony_ci				break;
224962306a36Sopenharmony_ci		}
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci		if (fifo >= BRCMF_FWS_FIFO_AC_BE &&
225262306a36Sopenharmony_ci		    fifo <= BRCMF_FWS_FIFO_AC_VO &&
225362306a36Sopenharmony_ci		    fws->fifo_credit[fifo] == 0 &&
225462306a36Sopenharmony_ci		    !fws->bus_flow_blocked) {
225562306a36Sopenharmony_ci			while (brcmf_fws_borrow_credit(fws,
225662306a36Sopenharmony_ci						       fifo - 1, fifo,
225762306a36Sopenharmony_ci						       true) == 0) {
225862306a36Sopenharmony_ci				skb = brcmf_fws_deq(fws, fifo);
225962306a36Sopenharmony_ci				if (!skb) {
226062306a36Sopenharmony_ci					brcmf_fws_return_credits(fws, fifo, 1);
226162306a36Sopenharmony_ci					break;
226262306a36Sopenharmony_ci				}
226362306a36Sopenharmony_ci				if (brcmf_fws_commit_skb(fws, fifo, skb))
226462306a36Sopenharmony_ci					break;
226562306a36Sopenharmony_ci				if (fws->bus_flow_blocked)
226662306a36Sopenharmony_ci					break;
226762306a36Sopenharmony_ci			}
226862306a36Sopenharmony_ci		}
226962306a36Sopenharmony_ci	}
227062306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
227162306a36Sopenharmony_ci}
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci#ifdef DEBUG
227462306a36Sopenharmony_cistatic int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
227562306a36Sopenharmony_ci{
227662306a36Sopenharmony_ci	struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
227762306a36Sopenharmony_ci	struct brcmf_fws_stats *fwstats = &(drvr_to_fws(bus_if->drvr)->stats);
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci	seq_printf(seq,
228062306a36Sopenharmony_ci		   "header_pulls:      %u\n"
228162306a36Sopenharmony_ci		   "header_only_pkt:   %u\n"
228262306a36Sopenharmony_ci		   "tlv_parse_failed:  %u\n"
228362306a36Sopenharmony_ci		   "tlv_invalid_type:  %u\n"
228462306a36Sopenharmony_ci		   "mac_update_fails:  %u\n"
228562306a36Sopenharmony_ci		   "ps_update_fails:   %u\n"
228662306a36Sopenharmony_ci		   "if_update_fails:   %u\n"
228762306a36Sopenharmony_ci		   "pkt2bus:           %u\n"
228862306a36Sopenharmony_ci		   "generic_error:     %u\n"
228962306a36Sopenharmony_ci		   "rollback_success:  %u\n"
229062306a36Sopenharmony_ci		   "rollback_failed:   %u\n"
229162306a36Sopenharmony_ci		   "delayq_full:       %u\n"
229262306a36Sopenharmony_ci		   "supprq_full:       %u\n"
229362306a36Sopenharmony_ci		   "txs_indicate:      %u\n"
229462306a36Sopenharmony_ci		   "txs_discard:       %u\n"
229562306a36Sopenharmony_ci		   "txs_suppr_core:    %u\n"
229662306a36Sopenharmony_ci		   "txs_suppr_ps:      %u\n"
229762306a36Sopenharmony_ci		   "txs_tossed:        %u\n"
229862306a36Sopenharmony_ci		   "txs_host_tossed:   %u\n"
229962306a36Sopenharmony_ci		   "bus_flow_block:    %u\n"
230062306a36Sopenharmony_ci		   "fws_flow_block:    %u\n"
230162306a36Sopenharmony_ci		   "send_pkts:         BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
230262306a36Sopenharmony_ci		   "requested_sent:    BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
230362306a36Sopenharmony_ci		   fwstats->header_pulls,
230462306a36Sopenharmony_ci		   fwstats->header_only_pkt,
230562306a36Sopenharmony_ci		   fwstats->tlv_parse_failed,
230662306a36Sopenharmony_ci		   fwstats->tlv_invalid_type,
230762306a36Sopenharmony_ci		   fwstats->mac_update_failed,
230862306a36Sopenharmony_ci		   fwstats->mac_ps_update_failed,
230962306a36Sopenharmony_ci		   fwstats->if_update_failed,
231062306a36Sopenharmony_ci		   fwstats->pkt2bus,
231162306a36Sopenharmony_ci		   fwstats->generic_error,
231262306a36Sopenharmony_ci		   fwstats->rollback_success,
231362306a36Sopenharmony_ci		   fwstats->rollback_failed,
231462306a36Sopenharmony_ci		   fwstats->delayq_full_error,
231562306a36Sopenharmony_ci		   fwstats->supprq_full_error,
231662306a36Sopenharmony_ci		   fwstats->txs_indicate,
231762306a36Sopenharmony_ci		   fwstats->txs_discard,
231862306a36Sopenharmony_ci		   fwstats->txs_supp_core,
231962306a36Sopenharmony_ci		   fwstats->txs_supp_ps,
232062306a36Sopenharmony_ci		   fwstats->txs_tossed,
232162306a36Sopenharmony_ci		   fwstats->txs_host_tossed,
232262306a36Sopenharmony_ci		   fwstats->bus_flow_block,
232362306a36Sopenharmony_ci		   fwstats->fws_flow_block,
232462306a36Sopenharmony_ci		   fwstats->send_pkts[0], fwstats->send_pkts[1],
232562306a36Sopenharmony_ci		   fwstats->send_pkts[2], fwstats->send_pkts[3],
232662306a36Sopenharmony_ci		   fwstats->send_pkts[4],
232762306a36Sopenharmony_ci		   fwstats->requested_sent[0],
232862306a36Sopenharmony_ci		   fwstats->requested_sent[1],
232962306a36Sopenharmony_ci		   fwstats->requested_sent[2],
233062306a36Sopenharmony_ci		   fwstats->requested_sent[3],
233162306a36Sopenharmony_ci		   fwstats->requested_sent[4]);
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci	return 0;
233462306a36Sopenharmony_ci}
233562306a36Sopenharmony_ci#else
233662306a36Sopenharmony_cistatic int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
233762306a36Sopenharmony_ci{
233862306a36Sopenharmony_ci	return 0;
233962306a36Sopenharmony_ci}
234062306a36Sopenharmony_ci#endif
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_cistruct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr)
234362306a36Sopenharmony_ci{
234462306a36Sopenharmony_ci	struct brcmf_fws_info *fws;
234562306a36Sopenharmony_ci	struct brcmf_if *ifp;
234662306a36Sopenharmony_ci	u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
234762306a36Sopenharmony_ci	int rc;
234862306a36Sopenharmony_ci	u32 mode;
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	fws = kzalloc(sizeof(*fws), GFP_KERNEL);
235162306a36Sopenharmony_ci	if (!fws) {
235262306a36Sopenharmony_ci		rc = -ENOMEM;
235362306a36Sopenharmony_ci		goto fail;
235462306a36Sopenharmony_ci	}
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci	spin_lock_init(&fws->spinlock);
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci	/* store drvr reference */
235962306a36Sopenharmony_ci	fws->drvr = drvr;
236062306a36Sopenharmony_ci	fws->fcmode = drvr->settings->fcmode;
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	if (!drvr->bus_if->always_use_fws_queue &&
236362306a36Sopenharmony_ci	    (fws->fcmode == BRCMF_FWS_FCMODE_NONE)) {
236462306a36Sopenharmony_ci		fws->avoid_queueing = true;
236562306a36Sopenharmony_ci		brcmf_dbg(INFO, "FWS queueing will be avoided\n");
236662306a36Sopenharmony_ci		return fws;
236762306a36Sopenharmony_ci	}
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
237062306a36Sopenharmony_ci	if (fws->fws_wq == NULL) {
237162306a36Sopenharmony_ci		bphy_err(drvr, "workqueue creation failed\n");
237262306a36Sopenharmony_ci		rc = -EBADF;
237362306a36Sopenharmony_ci		goto fail;
237462306a36Sopenharmony_ci	}
237562306a36Sopenharmony_ci	INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	/* enable firmware signalling if fcmode active */
237862306a36Sopenharmony_ci	if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
237962306a36Sopenharmony_ci		tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
238062306a36Sopenharmony_ci		       BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
238162306a36Sopenharmony_ci		       BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
238262306a36Sopenharmony_ci		       BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci	rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
238562306a36Sopenharmony_ci				 brcmf_fws_notify_credit_map);
238662306a36Sopenharmony_ci	if (rc < 0) {
238762306a36Sopenharmony_ci		bphy_err(drvr, "register credit map handler failed\n");
238862306a36Sopenharmony_ci		goto fail;
238962306a36Sopenharmony_ci	}
239062306a36Sopenharmony_ci	rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT,
239162306a36Sopenharmony_ci				 brcmf_fws_notify_bcmc_credit_support);
239262306a36Sopenharmony_ci	if (rc < 0) {
239362306a36Sopenharmony_ci		bphy_err(drvr, "register bcmc credit handler failed\n");
239462306a36Sopenharmony_ci		brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
239562306a36Sopenharmony_ci		goto fail;
239662306a36Sopenharmony_ci	}
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	/* Setting the iovar may fail if feature is unsupported
239962306a36Sopenharmony_ci	 * so leave the rc as is so driver initialization can
240062306a36Sopenharmony_ci	 * continue. Set mode back to none indicating not enabled.
240162306a36Sopenharmony_ci	 */
240262306a36Sopenharmony_ci	fws->fw_signals = true;
240362306a36Sopenharmony_ci	ifp = brcmf_get_ifp(drvr, 0);
240462306a36Sopenharmony_ci	if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
240562306a36Sopenharmony_ci		bphy_err(drvr, "failed to set bdcv2 tlv signaling\n");
240662306a36Sopenharmony_ci		fws->fcmode = BRCMF_FWS_FCMODE_NONE;
240762306a36Sopenharmony_ci		fws->fw_signals = false;
240862306a36Sopenharmony_ci	}
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
241162306a36Sopenharmony_ci		brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	/* Enable seq number reuse, if supported */
241462306a36Sopenharmony_ci	if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
241562306a36Sopenharmony_ci		if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
241662306a36Sopenharmony_ci			mode = 0;
241762306a36Sopenharmony_ci			BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
241862306a36Sopenharmony_ci			if (brcmf_fil_iovar_int_set(ifp,
241962306a36Sopenharmony_ci						    "wlfc_mode", mode) == 0) {
242062306a36Sopenharmony_ci				BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
242162306a36Sopenharmony_ci			}
242262306a36Sopenharmony_ci		}
242362306a36Sopenharmony_ci	}
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	brcmf_fws_hanger_init(&fws->hanger);
242662306a36Sopenharmony_ci	brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
242762306a36Sopenharmony_ci	brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
242862306a36Sopenharmony_ci	brcmf_dbg(INFO, "added %s\n", fws->desc.other.name);
242962306a36Sopenharmony_ci	brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
243062306a36Sopenharmony_ci			BRCMF_FWS_PSQ_LEN);
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
243362306a36Sopenharmony_ci		  fws->fw_signals ? "enabled" : "disabled", tlv);
243462306a36Sopenharmony_ci	return fws;
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_cifail:
243762306a36Sopenharmony_ci	brcmf_fws_detach(fws);
243862306a36Sopenharmony_ci	return ERR_PTR(rc);
243962306a36Sopenharmony_ci}
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_civoid brcmf_fws_detach(struct brcmf_fws_info *fws)
244262306a36Sopenharmony_ci{
244362306a36Sopenharmony_ci	if (!fws)
244462306a36Sopenharmony_ci		return;
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	if (fws->fws_wq)
244762306a36Sopenharmony_ci		destroy_workqueue(fws->fws_wq);
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	/* cleanup */
245062306a36Sopenharmony_ci	brcmf_fws_lock(fws);
245162306a36Sopenharmony_ci	brcmf_fws_cleanup(fws, -1);
245262306a36Sopenharmony_ci	brcmf_fws_unlock(fws);
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	/* free top structure */
245562306a36Sopenharmony_ci	kfree(fws);
245662306a36Sopenharmony_ci}
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_civoid brcmf_fws_debugfs_create(struct brcmf_pub *drvr)
245962306a36Sopenharmony_ci{
246062306a36Sopenharmony_ci	/* create debugfs file for statistics */
246162306a36Sopenharmony_ci	brcmf_debugfs_add_entry(drvr, "fws_stats",
246262306a36Sopenharmony_ci				brcmf_debugfs_fws_stats_read);
246362306a36Sopenharmony_ci}
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_cibool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws)
246662306a36Sopenharmony_ci{
246762306a36Sopenharmony_ci	return !fws->avoid_queueing;
246862306a36Sopenharmony_ci}
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_cibool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
247162306a36Sopenharmony_ci{
247262306a36Sopenharmony_ci	if (!fws->creditmap_received)
247362306a36Sopenharmony_ci		return false;
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
247662306a36Sopenharmony_ci}
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_civoid brcmf_fws_bustxcomplete(struct brcmf_fws_info *fws, struct sk_buff *skb,
247962306a36Sopenharmony_ci			     bool success)
248062306a36Sopenharmony_ci{
248162306a36Sopenharmony_ci	u32 hslot;
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
248462306a36Sopenharmony_ci		brcmu_pkt_buf_free_skb(skb);
248562306a36Sopenharmony_ci		return;
248662306a36Sopenharmony_ci	}
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	if (!success) {
248962306a36Sopenharmony_ci		brcmf_fws_lock(fws);
249062306a36Sopenharmony_ci		hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
249162306a36Sopenharmony_ci		brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot,
249262306a36Sopenharmony_ci				      0, 0, 1);
249362306a36Sopenharmony_ci		brcmf_fws_unlock(fws);
249462306a36Sopenharmony_ci	}
249562306a36Sopenharmony_ci}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_civoid brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
249862306a36Sopenharmony_ci{
249962306a36Sopenharmony_ci	struct brcmf_fws_info *fws = drvr_to_fws(drvr);
250062306a36Sopenharmony_ci	struct brcmf_if *ifp;
250162306a36Sopenharmony_ci	int i;
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci	if (fws->avoid_queueing) {
250462306a36Sopenharmony_ci		for (i = 0; i < BRCMF_MAX_IFS; i++) {
250562306a36Sopenharmony_ci			ifp = drvr->iflist[i];
250662306a36Sopenharmony_ci			if (!ifp || !ifp->ndev)
250762306a36Sopenharmony_ci				continue;
250862306a36Sopenharmony_ci			brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW,
250962306a36Sopenharmony_ci					     flow_blocked);
251062306a36Sopenharmony_ci		}
251162306a36Sopenharmony_ci	} else {
251262306a36Sopenharmony_ci		fws->bus_flow_blocked = flow_blocked;
251362306a36Sopenharmony_ci		if (!flow_blocked)
251462306a36Sopenharmony_ci			brcmf_fws_schedule_deq(fws);
251562306a36Sopenharmony_ci		else
251662306a36Sopenharmony_ci			fws->stats.bus_flow_block++;
251762306a36Sopenharmony_ci	}
251862306a36Sopenharmony_ci}
2519