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