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