Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmfmac/fwsignal.c
178665 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2010 Broadcom Corporation3*/4#include <linux/types.h>5#include <linux/module.h>6#include <linux/if_ether.h>7#include <linux/spinlock.h>8#include <linux/skbuff.h>9#include <linux/netdevice.h>10#include <linux/etherdevice.h>11#include <linux/err.h>12#include <linux/jiffies.h>13#include <net/cfg80211.h>1415#include <brcmu_utils.h>16#include <brcmu_wifi.h>17#include "core.h"18#include "debug.h"19#include "bus.h"20#include "fwil.h"21#include "fwil_types.h"22#include "fweh.h"23#include "fwsignal.h"24#include "p2p.h"25#include "cfg80211.h"26#include "proto.h"27#include "bcdc.h"28#include "common.h"2930/**31* DOC: Firmware Signalling32*33* Firmware can send signals to host and vice versa, which are passed in the34* data packets using TLV based header. This signalling layer is on top of the35* BDC bus protocol layer.36*/3738/*39* single definition for firmware-driver flow control tlv's.40*41* each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length).42* A length value 0 indicates variable length tlv.43*/44#define BRCMF_FWS_TLV_DEFLIST \45BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \46BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \47BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \48BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \49BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \50BRCMF_FWS_TLV_DEF(MACDESC_ADD, 6, 8) \51BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \52BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \53BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \54BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \55BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \56BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \57BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \58BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \59BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \60BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \61BRCMF_FWS_TLV_DEF(FILLER, 255, 0)6263/*64* enum brcmf_fws_tlv_type - definition of tlv identifiers.65*/66#define BRCMF_FWS_TLV_DEF(name, id, len) \67BRCMF_FWS_TYPE_ ## name = id,68enum brcmf_fws_tlv_type {69BRCMF_FWS_TLV_DEFLIST70BRCMF_FWS_TYPE_INVALID71};72#undef BRCMF_FWS_TLV_DEF7374/*75* enum brcmf_fws_tlv_len - definition of tlv lengths.76*/77#define BRCMF_FWS_TLV_DEF(name, id, len) \78BRCMF_FWS_TYPE_ ## name ## _LEN = (len),79enum brcmf_fws_tlv_len {80BRCMF_FWS_TLV_DEFLIST81};82#undef BRCMF_FWS_TLV_DEF8384/* AMPDU rx reordering definitions */85#define BRCMF_RXREORDER_FLOWID_OFFSET 086#define BRCMF_RXREORDER_MAXIDX_OFFSET 287#define BRCMF_RXREORDER_FLAGS_OFFSET 488#define BRCMF_RXREORDER_CURIDX_OFFSET 689#define BRCMF_RXREORDER_EXPIDX_OFFSET 89091#define BRCMF_RXREORDER_DEL_FLOW 0x0192#define BRCMF_RXREORDER_FLUSH_ALL 0x0293#define BRCMF_RXREORDER_CURIDX_VALID 0x0494#define BRCMF_RXREORDER_EXPIDX_VALID 0x0895#define BRCMF_RXREORDER_NEW_HOLE 0x109697#ifdef DEBUG98/*99* brcmf_fws_tlv_names - array of tlv names.100*/101#define BRCMF_FWS_TLV_DEF(name, id, len) \102{ id, #name },103static struct {104enum brcmf_fws_tlv_type id;105const char *name;106} brcmf_fws_tlv_names[] = {107BRCMF_FWS_TLV_DEFLIST108};109#undef BRCMF_FWS_TLV_DEF110111112static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)113{114int i;115116for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++)117if (brcmf_fws_tlv_names[i].id == id)118return brcmf_fws_tlv_names[i].name;119120return "INVALID";121}122#else123static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)124{125return "NODEBUG";126}127#endif /* DEBUG */128129/*130* The PKTTAG tlv has additional bytes when firmware-signalling131* mode has REUSESEQ flag set.132*/133#define BRCMF_FWS_TYPE_SEQ_LEN 2134135/*136* flags used to enable tlv signalling from firmware.137*/138#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001139#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002140#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004141#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008142#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010143#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020144#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040145146#define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32147#define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff148149#define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0150#define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1151#define BRCMF_FWS_FLOWCONTROL_HIWATER 128152#define BRCMF_FWS_FLOWCONTROL_LOWATER 64153154#define BRCMF_FWS_PSQ_PREC_COUNT ((BRCMF_FWS_FIFO_COUNT + 1) * 2)155#define BRCMF_FWS_PSQ_LEN 256156157#define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01158#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02159160#define BRCMF_FWS_RET_OK_NOSCHEDULE 0161#define BRCMF_FWS_RET_OK_SCHEDULE 1162163#define BRCMF_FWS_MODE_REUSESEQ_SHIFT 3 /* seq reuse */164#define BRCMF_FWS_MODE_SET_REUSESEQ(x, val) ((x) = \165((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \166(((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT))167#define BRCMF_FWS_MODE_GET_REUSESEQ(x) \168(((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1)169170/**171* enum brcmf_fws_skb_state - indicates processing state of skb.172*173* @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver.174* @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue.175* @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware.176* @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info.177*/178enum brcmf_fws_skb_state {179BRCMF_FWS_SKBSTATE_NEW,180BRCMF_FWS_SKBSTATE_DELAYED,181BRCMF_FWS_SKBSTATE_SUPPRESSED,182BRCMF_FWS_SKBSTATE_TIM183};184185/**186* struct brcmf_skbuff_cb - control buffer associated with skbuff.187*188* @bus_flags: 2 bytes reserved for bus specific parameters189* @if_flags: holds interface index and packet related flags.190* @htod: host to device packet identifier (used in PKTTAG tlv).191* @htod_seq: this 16-bit is original seq number for every suppress packet.192* @state: transmit state of the packet.193* @mac: descriptor related to destination for this packet.194*195* This information is stored in control buffer struct sk_buff::cb, which196* provides 48 bytes of storage so this structure should not exceed that.197*/198struct brcmf_skbuff_cb {199u16 bus_flags;200u16 if_flags;201u32 htod;202u16 htod_seq;203enum brcmf_fws_skb_state state;204struct brcmf_fws_mac_descriptor *mac;205};206207/*208* macro casting skbuff control buffer to struct brcmf_skbuff_cb.209*/210#define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb))211212/*213* sk_buff control if flags214*215* b[11] - packet sent upon firmware request.216* b[10] - packet only contains signalling data.217* b[9] - packet is a tx packet.218* b[8] - packet used requested credit219* b[7] - interface in AP mode.220* b[3:0] - interface index.221*/222#define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800223#define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11224#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400225#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT 10226#define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK 0x0200227#define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT 9228#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK 0x0100229#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT 8230#define BRCMF_SKB_IF_FLAGS_IF_AP_MASK 0x0080231#define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT 7232#define BRCMF_SKB_IF_FLAGS_INDEX_MASK 0x000f233#define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0234235#define brcmf_skb_if_flags_set_field(skb, field, value) \236brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \237BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \238BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value))239#define brcmf_skb_if_flags_get_field(skb, field) \240brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \241BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \242BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT)243244/*245* sk_buff control packet identifier246*247* 32-bit packet identifier used in PKTTAG tlv from host to dongle.248*249* - Generated at the host (e.g. dhd)250* - Seen as a generic sequence number by firmware except for the flags field.251*252* Generation : b[31] => generation number for this packet [host->fw]253* OR, current generation number [fw->host]254* Flags : b[30:27] => command, status flags255* FIFO-AC : b[26:24] => AC-FIFO id256* h-slot : b[23:8] => hanger-slot257* freerun : b[7:0] => A free running counter258*/259#define BRCMF_SKB_HTOD_TAG_GENERATION_MASK 0x80000000260#define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT 31261#define BRCMF_SKB_HTOD_TAG_FLAGS_MASK 0x78000000262#define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT 27263#define BRCMF_SKB_HTOD_TAG_FIFO_MASK 0x07000000264#define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT 24265#define BRCMF_SKB_HTOD_TAG_HSLOT_MASK 0x00ffff00266#define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT 8267#define BRCMF_SKB_HTOD_TAG_FREERUN_MASK 0x000000ff268#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0269270#define brcmf_skb_htod_tag_set_field(skb, field, value) \271brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \272BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \273BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value))274#define brcmf_skb_htod_tag_get_field(skb, field) \275brcmu_maskget32(brcmf_skbcb(skb)->htod, \276BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \277BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT)278279#define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK 0x2000280#define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT 13281#define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK 0x1000282#define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT 12283#define BRCMF_SKB_HTOD_SEQ_NR_MASK 0x0fff284#define BRCMF_SKB_HTOD_SEQ_NR_SHIFT 0285286#define brcmf_skb_htod_seq_set_field(skb, field, value) \287brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \288BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \289BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value))290#define brcmf_skb_htod_seq_get_field(skb, field) \291brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \292BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \293BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT)294295#define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000296#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31297#define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000298#define BRCMF_FWS_TXSTAT_FLAGS_SHIFT 27299#define BRCMF_FWS_TXSTAT_FIFO_MASK 0x07000000300#define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24301#define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00302#define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8303#define BRCMF_FWS_TXSTAT_FREERUN_MASK 0x000000FF304#define BRCMF_FWS_TXSTAT_FREERUN_SHIFT 0305306#define brcmf_txstatus_get_field(txs, field) \307brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \308BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)309310/* How long to defer borrowing in jiffies */311#define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10)312313314/**315* enum brcmf_fws_txstatus - txstatus flag values.316*317* @BRCMF_FWS_TXSTATUS_DISCARD:318* host is free to discard the packet.319* @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS:320* 802.11 core suppressed the packet.321* @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS:322* firmware suppress the packet as device is already in PS mode.323* @BRCMF_FWS_TXSTATUS_FW_TOSSED:324* firmware tossed the packet.325* @BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK:326* firmware tossed the packet after retries.327* @BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED:328* firmware wrongly reported suppressed previously, now fixing to acked.329* @BRCMF_FWS_TXSTATUS_HOST_TOSSED:330* host tossed the packet.331*/332enum brcmf_fws_txstatus {333BRCMF_FWS_TXSTATUS_DISCARD,334BRCMF_FWS_TXSTATUS_CORE_SUPPRESS,335BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS,336BRCMF_FWS_TXSTATUS_FW_TOSSED,337BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK,338BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED,339BRCMF_FWS_TXSTATUS_HOST_TOSSED340};341342enum brcmf_fws_fcmode {343BRCMF_FWS_FCMODE_NONE,344BRCMF_FWS_FCMODE_IMPLIED_CREDIT,345BRCMF_FWS_FCMODE_EXPLICIT_CREDIT346};347348enum brcmf_fws_mac_desc_state {349BRCMF_FWS_STATE_OPEN = 1,350BRCMF_FWS_STATE_CLOSE351};352353/**354* struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface355*356* @name: name of the descriptor.357* @occupied: slot is in use.358* @mac_handle: handle for mac entry determined by firmware.359* @interface_id: interface index.360* @state: current state.361* @suppressed: mac entry is suppressed.362* @generation: generation bit.363* @ac_bitmap: ac queue bitmap.364* @requested_credit: credits requested by firmware.365* @requested_packet: packet requested by firmware.366* @ea: ethernet address.367* @seq: per-node free-running sequence.368* @psq: power-save queue.369* @transit_count: packet in transit to firmware.370* @suppr_transit_count: suppressed packet in transit to firmware.371* @send_tim_signal: if set tim signal will be sent.372* @traffic_pending_bmp: traffic pending bitmap.373* @traffic_lastreported_bmp: traffic last reported bitmap.374*/375struct brcmf_fws_mac_descriptor {376char name[16];377u8 occupied;378u8 mac_handle;379u8 interface_id;380u8 state;381bool suppressed;382u8 generation;383u8 ac_bitmap;384u8 requested_credit;385u8 requested_packet;386u8 ea[ETH_ALEN];387u8 seq[BRCMF_FWS_FIFO_COUNT];388struct pktq psq;389int transit_count;390int suppr_transit_count;391bool send_tim_signal;392u8 traffic_pending_bmp;393u8 traffic_lastreported_bmp;394};395396#define BRCMF_FWS_HANGER_MAXITEMS 3072397#define BRCMF_BORROW_RATIO 3398399/**400* enum brcmf_fws_hanger_item_state - state of hanger item.401*402* @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use.403* @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use.404* @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed.405*/406enum brcmf_fws_hanger_item_state {407BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1,408BRCMF_FWS_HANGER_ITEM_STATE_INUSE,409BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED410};411412413/**414* struct brcmf_fws_hanger_item - single entry for tx pending packet.415*416* @state: entry is either free or occupied.417* @pkt: packet itself.418*/419struct brcmf_fws_hanger_item {420enum brcmf_fws_hanger_item_state state;421struct sk_buff *pkt;422};423424/**425* struct brcmf_fws_hanger - holds packets awaiting firmware txstatus.426*427* @pushed: packets pushed to await txstatus.428* @popped: packets popped upon handling txstatus.429* @failed_to_push: packets that could not be pushed.430* @failed_to_pop: packets that could not be popped.431* @failed_slotfind: packets for which failed to find an entry.432* @slot_pos: last returned item index for a free entry.433* @items: array of hanger items.434*/435struct brcmf_fws_hanger {436u32 pushed;437u32 popped;438u32 failed_to_push;439u32 failed_to_pop;440u32 failed_slotfind;441u32 slot_pos;442struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS];443};444445struct brcmf_fws_macdesc_table {446struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE];447struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS];448struct brcmf_fws_mac_descriptor other;449};450451struct brcmf_fws_stats {452u32 tlv_parse_failed;453u32 tlv_invalid_type;454u32 header_only_pkt;455u32 header_pulls;456u32 pkt2bus;457u32 send_pkts[5];458u32 requested_sent[5];459u32 generic_error;460u32 mac_update_failed;461u32 mac_ps_update_failed;462u32 if_update_failed;463u32 packet_request_failed;464u32 credit_request_failed;465u32 rollback_success;466u32 rollback_failed;467u32 delayq_full_error;468u32 supprq_full_error;469u32 txs_indicate;470u32 txs_discard;471u32 txs_supp_core;472u32 txs_supp_ps;473u32 txs_tossed;474u32 txs_host_tossed;475u32 bus_flow_block;476u32 fws_flow_block;477};478479struct brcmf_fws_info {480struct brcmf_pub *drvr;481spinlock_t spinlock;482ulong flags;483struct brcmf_fws_stats stats;484struct brcmf_fws_hanger hanger;485enum brcmf_fws_fcmode fcmode;486bool fw_signals;487bool bcmc_credit_check;488struct brcmf_fws_macdesc_table desc;489struct workqueue_struct *fws_wq;490struct work_struct fws_dequeue_work;491u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];492int fifo_credit[BRCMF_FWS_FIFO_COUNT];493int init_fifo_credit[BRCMF_FWS_FIFO_COUNT];494int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1]495[BRCMF_FWS_FIFO_AC_VO + 1];496int deq_node_pos[BRCMF_FWS_FIFO_COUNT];497u32 fifo_credit_map;498u32 fifo_delay_map;499unsigned long borrow_defer_timestamp;500bool bus_flow_blocked;501bool creditmap_received;502u8 mode;503bool avoid_queueing;504};505506#define BRCMF_FWS_TLV_DEF(name, id, len) \507case BRCMF_FWS_TYPE_ ## name: \508return len;509510/**511* brcmf_fws_get_tlv_len() - returns defined length for given tlv id.512*513* @fws: firmware-signalling information.514* @id: identifier of the TLV.515*516* Return: the specified length for the given TLV; Otherwise -EINVAL.517*/518static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,519enum brcmf_fws_tlv_type id)520{521switch (id) {522BRCMF_FWS_TLV_DEFLIST523default:524fws->stats.tlv_invalid_type++;525break;526}527return -EINVAL;528}529#undef BRCMF_FWS_TLV_DEF530531static void brcmf_fws_lock(struct brcmf_fws_info *fws)532__acquires(&fws->spinlock)533{534spin_lock_irqsave(&fws->spinlock, fws->flags);535}536537static void brcmf_fws_unlock(struct brcmf_fws_info *fws)538__releases(&fws->spinlock)539{540spin_unlock_irqrestore(&fws->spinlock, fws->flags);541}542543static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)544{545u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);546return ifidx == *(int *)arg;547}548549static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)550{551int i;552553memset(hanger, 0, sizeof(*hanger));554for (i = 0; i < ARRAY_SIZE(hanger->items); i++)555hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;556}557558static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)559{560u32 i;561562i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;563564while (i != h->slot_pos) {565if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {566h->slot_pos = i;567goto done;568}569i++;570if (i == BRCMF_FWS_HANGER_MAXITEMS)571i = 0;572}573brcmf_err("all slots occupied\n");574h->failed_slotfind++;575i = BRCMF_FWS_HANGER_MAXITEMS;576done:577return i;578}579580static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,581struct sk_buff *pkt, u32 slot_id)582{583if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)584return -ENOENT;585586if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) {587brcmf_err("slot is not free\n");588h->failed_to_push++;589return -EINVAL;590}591592h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE;593h->items[slot_id].pkt = pkt;594h->pushed++;595return 0;596}597598static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,599u32 slot_id, struct sk_buff **pktout,600bool remove_item)601{602if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)603return -ENOENT;604605if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {606brcmf_err("entry not in use\n");607h->failed_to_pop++;608return -EINVAL;609}610611*pktout = h->items[slot_id].pkt;612if (remove_item) {613h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;614h->items[slot_id].pkt = NULL;615h->popped++;616}617return 0;618}619620static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q,621int ifidx)622{623struct brcmf_fws_hanger_item *hi;624bool (*matchfn)(struct sk_buff *, void *) = NULL;625struct sk_buff *skb;626int prec;627u32 hslot;628629if (ifidx != -1)630matchfn = brcmf_fws_ifidx_match;631for (prec = 0; prec < q->num_prec; prec++) {632skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);633while (skb) {634hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);635hi = &fws->hanger.items[hslot];636WARN_ON(skb != hi->pkt);637hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;638brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,639true);640brcmu_pkt_buf_free_skb(skb);641skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);642}643}644}645646static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,647u32 slot_id)648{649if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)650return -ENOENT;651652if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {653brcmf_err("entry not in use\n");654return -EINVAL;655}656657h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED;658return 0;659}660661static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,662bool (*fn)(struct sk_buff *, void *),663int ifidx)664{665struct brcmf_fws_hanger *h = &fws->hanger;666struct sk_buff *skb;667int i;668enum brcmf_fws_hanger_item_state s;669670for (i = 0; i < ARRAY_SIZE(h->items); i++) {671s = h->items[i].state;672if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||673s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {674skb = h->items[i].pkt;675if (fn == NULL || fn(skb, &ifidx)) {676/* suppress packets freed from psq */677if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE)678brcmu_pkt_buf_free_skb(skb);679h->items[i].state =680BRCMF_FWS_HANGER_ITEM_STATE_FREE;681}682}683}684}685686static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws,687struct brcmf_fws_mac_descriptor *desc)688{689if (desc == &fws->desc.other)690strscpy(desc->name, "MAC-OTHER", sizeof(desc->name));691else if (desc->mac_handle)692scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d",693desc->mac_handle, desc->interface_id);694else695scnprintf(desc->name, sizeof(desc->name), "MACIF:%d",696desc->interface_id);697}698699static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc,700u8 *addr, u8 ifidx)701{702brcmf_dbg(TRACE,703"enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx);704desc->occupied = 1;705desc->state = BRCMF_FWS_STATE_OPEN;706desc->requested_credit = 0;707desc->requested_packet = 0;708/* depending on use may need ifp->bsscfgidx instead */709desc->interface_id = ifidx;710desc->ac_bitmap = 0xff; /* update this when handling APSD */711if (addr)712memcpy(&desc->ea[0], addr, ETH_ALEN);713}714715static716void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc)717{718brcmf_dbg(TRACE,719"enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id);720desc->occupied = 0;721desc->state = BRCMF_FWS_STATE_CLOSE;722desc->requested_credit = 0;723desc->requested_packet = 0;724}725726static struct brcmf_fws_mac_descriptor *727brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)728{729struct brcmf_fws_mac_descriptor *entry;730int i;731732if (ea == NULL)733return ERR_PTR(-EINVAL);734735entry = &fws->desc.nodes[0];736for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) {737if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN))738return entry;739entry++;740}741742return ERR_PTR(-ENOENT);743}744745static struct brcmf_fws_mac_descriptor*746brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da)747{748struct brcmf_fws_mac_descriptor *entry;749bool multicast;750751multicast = is_multicast_ether_addr(da);752753/* Multicast destination, STA and P2P clients get the interface entry.754* STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations755* have their own entry.756*/757if (multicast && ifp->fws_desc) {758entry = ifp->fws_desc;759goto done;760}761762entry = brcmf_fws_macdesc_lookup(fws, da);763if (IS_ERR(entry))764entry = ifp->fws_desc;765766done:767return entry;768}769770static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws,771struct brcmf_fws_mac_descriptor *entry,772int fifo)773{774struct brcmf_fws_mac_descriptor *if_entry;775bool closed;776777/* for unique destination entries the related interface778* may be closed.779*/780if (entry->mac_handle) {781if_entry = &fws->desc.iface[entry->interface_id];782if (if_entry->state == BRCMF_FWS_STATE_CLOSE)783return true;784}785/* an entry is closed when the state is closed and786* the firmware did not request anything.787*/788closed = entry->state == BRCMF_FWS_STATE_CLOSE &&789!entry->requested_credit && !entry->requested_packet;790791/* Or firmware does not allow traffic for given fifo */792return closed || !(entry->ac_bitmap & BIT(fifo));793}794795static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,796struct brcmf_fws_mac_descriptor *entry,797int ifidx)798{799if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {800brcmf_fws_psq_flush(fws, &entry->psq, ifidx);801entry->occupied = !!(entry->psq.len);802}803}804805static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,806bool (*fn)(struct sk_buff *, void *),807int ifidx)808{809struct brcmf_fws_hanger_item *hi;810struct pktq *txq;811struct sk_buff *skb;812int prec;813u32 hslot;814815txq = brcmf_bus_gettxq(fws->drvr->bus_if);816if (IS_ERR(txq)) {817brcmf_dbg(TRACE, "no txq to clean up\n");818return;819}820821for (prec = 0; prec < txq->num_prec; prec++) {822skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);823while (skb) {824hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);825hi = &fws->hanger.items[hslot];826WARN_ON(skb != hi->pkt);827hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;828brcmu_pkt_buf_free_skb(skb);829skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);830}831}832}833834static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)835{836int i;837struct brcmf_fws_mac_descriptor *table;838bool (*matchfn)(struct sk_buff *, void *) = NULL;839840if (fws == NULL)841return;842843if (ifidx != -1)844matchfn = brcmf_fws_ifidx_match;845846/* cleanup individual nodes */847table = &fws->desc.nodes[0];848for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)849brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx);850851brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx);852brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx);853brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);854}855856static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)857{858struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;859u8 *wlh;860u16 data_offset = 0;861u8 fillers;862__le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);863__le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq);864865brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n",866entry->name, brcmf_skb_if_flags_get_field(skb, INDEX),867(le32_to_cpu(pkttag) >> 8) & 0xffff,868brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq);869if (entry->send_tim_signal)870data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;871if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))872data_offset += BRCMF_FWS_TYPE_SEQ_LEN;873/* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */874data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;875fillers = round_up(data_offset, 4) - data_offset;876data_offset += fillers;877878skb_push(skb, data_offset);879wlh = skb->data;880881wlh[0] = BRCMF_FWS_TYPE_PKTTAG;882wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;883memcpy(&wlh[2], &pkttag, sizeof(pkttag));884if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {885wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN;886memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq,887sizeof(pktseq));888}889wlh += wlh[1] + 2;890891if (entry->send_tim_signal) {892entry->send_tim_signal = false;893wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;894wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;895wlh[2] = entry->mac_handle;896wlh[3] = entry->traffic_pending_bmp;897brcmf_dbg(TRACE, "adding TIM info: handle %d bmp 0x%X\n",898entry->mac_handle, entry->traffic_pending_bmp);899wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;900entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;901}902if (fillers)903memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);904905return (u8)(data_offset >> 2);906}907908static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,909struct brcmf_fws_mac_descriptor *entry,910int fifo, bool send_immediately)911{912struct sk_buff *skb;913struct brcmf_skbuff_cb *skcb;914s32 err;915u32 len;916u8 data_offset;917int ifidx;918919/* check delayedQ and suppressQ in one call using bitmap */920if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)921entry->traffic_pending_bmp &= ~NBITVAL(fifo);922else923entry->traffic_pending_bmp |= NBITVAL(fifo);924925entry->send_tim_signal = false;926if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)927entry->send_tim_signal = true;928if (send_immediately && entry->send_tim_signal &&929entry->state == BRCMF_FWS_STATE_CLOSE) {930/* create a dummy packet and sent that. The traffic */931/* bitmap info will automatically be attached to that packet */932len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +933BRCMF_FWS_TYPE_SEQ_LEN +934BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +9354 + fws->drvr->hdrlen;936skb = brcmu_pkt_buf_get_skb(len);937if (skb == NULL)938return false;939skb_pull(skb, len);940skcb = brcmf_skbcb(skb);941skcb->mac = entry;942skcb->state = BRCMF_FWS_SKBSTATE_TIM;943skcb->htod = 0;944skcb->htod_seq = 0;945data_offset = brcmf_fws_hdrpush(fws, skb);946ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);947brcmf_fws_unlock(fws);948err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);949brcmf_fws_lock(fws);950if (err)951brcmu_pkt_buf_free_skb(skb);952return true;953}954return false;955}956957static void958brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,959u8 if_id)960{961struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);962963if (WARN_ON(!ifp))964return;965966if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&967pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER)968brcmf_txflowblock_if(ifp,969BRCMF_NETIF_STOP_REASON_FWS_FC, false);970if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&971pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) {972fws->stats.fws_flow_block++;973brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);974}975return;976}977978static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)979{980brcmf_dbg(CTL, "rssi %d\n", rssi);981return 0;982}983984static985int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)986{987struct brcmf_fws_mac_descriptor *entry, *existing;988u8 mac_handle;989u8 ifidx;990u8 *addr;991992mac_handle = *data++;993ifidx = *data++;994addr = data;995996entry = &fws->desc.nodes[mac_handle & 0x1F];997if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {998if (entry->occupied) {999brcmf_dbg(TRACE, "deleting %s mac %pM\n",1000entry->name, addr);1001brcmf_fws_lock(fws);1002brcmf_fws_macdesc_cleanup(fws, entry, -1);1003brcmf_fws_macdesc_deinit(entry);1004brcmf_fws_unlock(fws);1005} else1006fws->stats.mac_update_failed++;1007return 0;1008}10091010existing = brcmf_fws_macdesc_lookup(fws, addr);1011if (IS_ERR(existing)) {1012if (!entry->occupied) {1013brcmf_fws_lock(fws);1014entry->mac_handle = mac_handle;1015brcmf_fws_macdesc_init(entry, addr, ifidx);1016brcmf_fws_macdesc_set_name(fws, entry);1017brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,1018BRCMF_FWS_PSQ_LEN);1019brcmf_fws_unlock(fws);1020brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);1021} else {1022fws->stats.mac_update_failed++;1023}1024} else {1025if (entry != existing) {1026brcmf_dbg(TRACE, "copy mac %s\n", existing->name);1027brcmf_fws_lock(fws);1028memcpy(entry, existing,1029offsetof(struct brcmf_fws_mac_descriptor, psq));1030entry->mac_handle = mac_handle;1031brcmf_fws_macdesc_deinit(existing);1032brcmf_fws_macdesc_set_name(fws, entry);1033brcmf_fws_unlock(fws);1034brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,1035addr);1036} else {1037brcmf_dbg(TRACE, "use existing\n");1038WARN_ON(entry->mac_handle != mac_handle);1039/* TODO: what should we do here: continue, reinit, .. */1040}1041}1042return 0;1043}10441045static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,1046u8 type, u8 *data)1047{1048struct brcmf_fws_mac_descriptor *entry;1049u8 mac_handle;1050int ret;10511052mac_handle = data[0];1053entry = &fws->desc.nodes[mac_handle & 0x1F];1054if (!entry->occupied) {1055fws->stats.mac_ps_update_failed++;1056return -ESRCH;1057}1058brcmf_fws_lock(fws);1059/* a state update should wipe old credits */1060entry->requested_credit = 0;1061entry->requested_packet = 0;1062if (type == BRCMF_FWS_TYPE_MAC_OPEN) {1063entry->state = BRCMF_FWS_STATE_OPEN;1064ret = BRCMF_FWS_RET_OK_SCHEDULE;1065} else {1066entry->state = BRCMF_FWS_STATE_CLOSE;1067brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false);1068brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false);1069brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false);1070brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);1071ret = BRCMF_FWS_RET_OK_NOSCHEDULE;1072}1073brcmf_fws_unlock(fws);1074return ret;1075}10761077static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,1078u8 type, u8 *data)1079{1080struct brcmf_fws_mac_descriptor *entry;1081u8 ifidx;1082int ret;10831084ifidx = data[0];10851086if (ifidx >= BRCMF_MAX_IFS) {1087ret = -ERANGE;1088goto fail;1089}10901091entry = &fws->desc.iface[ifidx];1092if (!entry->occupied) {1093ret = -ESRCH;1094goto fail;1095}10961097brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,1098entry->name);1099brcmf_fws_lock(fws);1100switch (type) {1101case BRCMF_FWS_TYPE_INTERFACE_OPEN:1102entry->state = BRCMF_FWS_STATE_OPEN;1103ret = BRCMF_FWS_RET_OK_SCHEDULE;1104break;1105case BRCMF_FWS_TYPE_INTERFACE_CLOSE:1106entry->state = BRCMF_FWS_STATE_CLOSE;1107ret = BRCMF_FWS_RET_OK_NOSCHEDULE;1108break;1109default:1110ret = -EINVAL;1111brcmf_fws_unlock(fws);1112goto fail;1113}1114brcmf_fws_unlock(fws);1115return ret;11161117fail:1118fws->stats.if_update_failed++;1119return ret;1120}11211122static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,1123u8 *data)1124{1125struct brcmf_fws_mac_descriptor *entry;11261127entry = &fws->desc.nodes[data[1] & 0x1F];1128if (!entry->occupied) {1129if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)1130fws->stats.credit_request_failed++;1131else1132fws->stats.packet_request_failed++;1133return -ESRCH;1134}11351136brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",1137brcmf_fws_get_tlv_name(type), type, entry->name,1138data[0], data[2]);1139brcmf_fws_lock(fws);1140if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)1141entry->requested_credit = data[0];1142else1143entry->requested_packet = data[0];11441145entry->ac_bitmap = data[2];1146brcmf_fws_unlock(fws);1147return BRCMF_FWS_RET_OK_SCHEDULE;1148}11491150static void1151brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry,1152struct sk_buff *skb)1153{1154if (entry->requested_credit > 0) {1155entry->requested_credit--;1156brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);1157brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1);1158if (entry->state != BRCMF_FWS_STATE_CLOSE)1159brcmf_err("requested credit set while mac not closed!\n");1160} else if (entry->requested_packet > 0) {1161entry->requested_packet--;1162brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);1163brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);1164if (entry->state != BRCMF_FWS_STATE_CLOSE)1165brcmf_err("requested packet set while mac not closed!\n");1166} else {1167brcmf_skb_if_flags_set_field(skb, REQUESTED, 0);1168brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);1169}1170}11711172static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb)1173{1174struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;11751176if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) &&1177(entry->state == BRCMF_FWS_STATE_CLOSE))1178entry->requested_credit++;1179}11801181static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,1182u8 fifo, u8 credits)1183{1184int lender_ac;1185int *borrowed;1186int *fifo_credit;11871188if (!credits)1189return;11901191fws->fifo_credit_map |= 1 << fifo;11921193if (fifo > BRCMF_FWS_FIFO_AC_BK &&1194fifo <= BRCMF_FWS_FIFO_AC_VO) {1195for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;1196lender_ac--) {1197borrowed = &fws->credits_borrowed[fifo][lender_ac];1198if (*borrowed) {1199fws->fifo_credit_map |= (1 << lender_ac);1200fifo_credit = &fws->fifo_credit[lender_ac];1201if (*borrowed >= credits) {1202*borrowed -= credits;1203*fifo_credit += credits;1204return;1205} else {1206credits -= *borrowed;1207*fifo_credit += *borrowed;1208*borrowed = 0;1209}1210}1211}1212}12131214if (credits) {1215fws->fifo_credit[fifo] += credits;1216}12171218if (fws->fifo_credit[fifo] > fws->init_fifo_credit[fifo])1219fws->fifo_credit[fifo] = fws->init_fifo_credit[fifo];12201221}12221223static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)1224{1225/* only schedule dequeue when there are credits for delayed traffic */1226if ((fws->fifo_credit_map & fws->fifo_delay_map) ||1227(!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))1228queue_work(fws->fws_wq, &fws->fws_dequeue_work);1229}12301231static int brcmf_fws_enq(struct brcmf_fws_info *fws,1232enum brcmf_fws_skb_state state, int fifo,1233struct sk_buff *p)1234{1235struct brcmf_pub *drvr = fws->drvr;1236int prec = 2 * fifo;1237u32 *qfull_stat = &fws->stats.delayq_full_error;1238struct brcmf_fws_mac_descriptor *entry;1239struct pktq *pq;1240struct sk_buff_head *queue;1241struct sk_buff *p_head;1242struct sk_buff *p_tail;1243u32 fr_new;1244u32 fr_compare;12451246entry = brcmf_skbcb(p)->mac;1247if (entry == NULL) {1248bphy_err(drvr, "no mac descriptor found for skb %p\n", p);1249return -ENOENT;1250}12511252brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p);1253if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {1254prec += 1;1255qfull_stat = &fws->stats.supprq_full_error;12561257/* Fix out of order delivery of frames. Dont assume frame */1258/* can be inserted at the end, but look for correct position */1259pq = &entry->psq;1260if (pktq_full(pq) || pktq_pfull(pq, prec)) {1261*qfull_stat += 1;1262return -ENFILE;1263}1264queue = &pq->q[prec].skblist;12651266p_head = skb_peek(queue);1267p_tail = skb_peek_tail(queue);1268fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN);12691270while (p_head != p_tail) {1271fr_compare = brcmf_skb_htod_tag_get_field(p_tail,1272FREERUN);1273/* be sure to handle wrap of 256 */1274if (((fr_new > fr_compare) &&1275((fr_new - fr_compare) < 128)) ||1276((fr_new < fr_compare) &&1277((fr_compare - fr_new) > 128)))1278break;1279p_tail = skb_queue_prev(queue, p_tail);1280}1281/* Position found. Determine what to do */1282if (p_tail == NULL) {1283/* empty list */1284__skb_queue_tail(queue, p);1285} else {1286fr_compare = brcmf_skb_htod_tag_get_field(p_tail,1287FREERUN);1288if (((fr_new > fr_compare) &&1289((fr_new - fr_compare) < 128)) ||1290((fr_new < fr_compare) &&1291((fr_compare - fr_new) > 128))) {1292/* After tail */1293__skb_queue_after(queue, p_tail, p);1294} else {1295/* Before tail */1296__skb_insert(p, p_tail->prev, p_tail, queue);1297}1298}12991300/* Complete the counters and statistics */1301pq->len++;1302if (pq->hi_prec < prec)1303pq->hi_prec = (u8) prec;1304} else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) {1305*qfull_stat += 1;1306return -ENFILE;1307}13081309/* increment total enqueued packet count */1310fws->fifo_delay_map |= 1 << fifo;1311fws->fifo_enqpkt[fifo]++;13121313/* update the sk_buff state */1314brcmf_skbcb(p)->state = state;13151316/*1317* A packet has been pushed so update traffic1318* availability bitmap, if applicable1319*/1320brcmf_fws_tim_update(fws, entry, fifo, true);1321brcmf_fws_flow_control_check(fws, &entry->psq,1322brcmf_skb_if_flags_get_field(p, INDEX));1323return 0;1324}13251326static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)1327{1328struct brcmf_fws_mac_descriptor *table;1329struct brcmf_fws_mac_descriptor *entry;1330struct sk_buff *p;1331int num_nodes;1332int node_pos;1333int prec_out;1334int pmsk;1335int i;13361337table = (struct brcmf_fws_mac_descriptor *)&fws->desc;1338num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor);1339node_pos = fws->deq_node_pos[fifo];13401341for (i = 0; i < num_nodes; i++) {1342entry = &table[(node_pos + i) % num_nodes];1343if (!entry->occupied ||1344brcmf_fws_macdesc_closed(fws, entry, fifo))1345continue;13461347if (entry->suppressed)1348pmsk = 2;1349else1350pmsk = 3;1351p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);1352if (p == NULL) {1353if (entry->suppressed) {1354if (entry->suppr_transit_count)1355continue;1356entry->suppressed = false;1357p = brcmu_pktq_mdeq(&entry->psq,13581 << (fifo * 2), &prec_out);1359}1360}1361if (p == NULL)1362continue;13631364brcmf_fws_macdesc_use_req_credit(entry, p);13651366/* move dequeue position to ensure fair round-robin */1367fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes;1368brcmf_fws_flow_control_check(fws, &entry->psq,1369brcmf_skb_if_flags_get_field(p,1370INDEX)1371);1372/*1373* A packet has been picked up, update traffic1374* availability bitmap, if applicable1375*/1376brcmf_fws_tim_update(fws, entry, fifo, false);13771378/*1379* decrement total enqueued fifo packets and1380* clear delay bitmap if done.1381*/1382fws->fifo_enqpkt[fifo]--;1383if (fws->fifo_enqpkt[fifo] == 0)1384fws->fifo_delay_map &= ~(1 << fifo);1385goto done;1386}1387p = NULL;1388done:1389brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);1390return p;1391}13921393static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,1394struct sk_buff *skb,1395u32 genbit, u16 seq)1396{1397struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;1398u32 hslot;1399int ret;14001401hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);14021403/* this packet was suppressed */1404if (!entry->suppressed) {1405entry->suppressed = true;1406entry->suppr_transit_count = entry->transit_count;1407brcmf_dbg(DATA, "suppress %s: transit %d\n",1408entry->name, entry->transit_count);1409}14101411entry->generation = genbit;14121413brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);1414brcmf_skbcb(skb)->htod_seq = seq;1415if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {1416brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);1417brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);1418} else {1419brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);1420}1421ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);14221423if (ret != 0) {1424/* suppress q is full drop this packet */1425brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true);1426} else {1427/* Mark suppressed to avoid a double free during wlfc cleanup */1428brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);1429}14301431return ret;1432}14331434static int1435brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,1436u32 genbit, u16 seq, u8 compcnt)1437{1438struct brcmf_pub *drvr = fws->drvr;1439u32 fifo;1440u8 cnt = 0;1441int ret;1442bool remove_from_hanger = true;1443struct sk_buff *skb;1444struct brcmf_skbuff_cb *skcb;1445struct brcmf_fws_mac_descriptor *entry = NULL;1446struct brcmf_if *ifp;14471448brcmf_dbg(DATA, "flags %d\n", flags);14491450if (flags == BRCMF_FWS_TXSTATUS_DISCARD)1451fws->stats.txs_discard += compcnt;1452else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) {1453fws->stats.txs_supp_core += compcnt;1454remove_from_hanger = false;1455} else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) {1456fws->stats.txs_supp_ps += compcnt;1457remove_from_hanger = false;1458} else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)1459fws->stats.txs_tossed += compcnt;1460else if (flags == BRCMF_FWS_TXSTATUS_FW_DISCARD_NOACK)1461fws->stats.txs_discard += compcnt;1462else if (flags == BRCMF_FWS_TXSTATUS_FW_SUPPRESS_ACKED)1463fws->stats.txs_discard += compcnt;1464else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)1465fws->stats.txs_host_tossed += compcnt;1466else1467bphy_err(drvr, "unexpected txstatus\n");14681469while (cnt < compcnt) {1470ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,1471remove_from_hanger);1472if (ret != 0) {1473bphy_err(drvr, "no packet in hanger slot: hslot=%d\n",1474hslot);1475goto cont;1476}14771478skcb = brcmf_skbcb(skb);1479entry = skcb->mac;1480if (WARN_ON(!entry)) {1481brcmu_pkt_buf_free_skb(skb);1482goto cont;1483}1484entry->transit_count--;1485if (entry->suppressed && entry->suppr_transit_count)1486entry->suppr_transit_count--;14871488brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name,1489flags, skcb->htod, seq);14901491/* pick up the implicit credit from this packet */1492fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);1493if (fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT ||1494(brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||1495flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED) {1496brcmf_fws_return_credits(fws, fifo, 1);1497brcmf_fws_schedule_deq(fws);1498}1499brcmf_fws_macdesc_return_req_credit(skb);15001501ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);1502if (ret) {1503brcmu_pkt_buf_free_skb(skb);1504goto cont;1505}1506if (!remove_from_hanger)1507ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,1508genbit, seq);1509if (remove_from_hanger || ret)1510brcmf_txfinalize(ifp, skb, true);15111512cont:1513hslot = (hslot + 1) & (BRCMF_FWS_TXSTAT_HSLOT_MASK >>1514BRCMF_FWS_TXSTAT_HSLOT_SHIFT);1515if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))1516seq = (seq + 1) & BRCMF_SKB_HTOD_SEQ_NR_MASK;15171518cnt++;1519}15201521return 0;1522}15231524static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,1525u8 *data)1526{1527int i;15281529if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {1530brcmf_dbg(INFO, "ignored\n");1531return BRCMF_FWS_RET_OK_NOSCHEDULE;1532}15331534brcmf_dbg(DATA, "enter: data %pM\n", data);1535brcmf_fws_lock(fws);1536for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)1537brcmf_fws_return_credits(fws, i, data[i]);15381539brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,1540fws->fifo_delay_map);1541brcmf_fws_unlock(fws);1542return BRCMF_FWS_RET_OK_SCHEDULE;1543}15441545static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 type,1546u8 *data)1547{1548__le32 status_le;1549__le16 seq_le;1550u32 status;1551u32 hslot;1552u32 genbit;1553u8 flags;1554u16 seq;1555u8 compcnt;1556u8 compcnt_offset = BRCMF_FWS_TYPE_TXSTATUS_LEN;15571558memcpy(&status_le, data, sizeof(status_le));1559status = le32_to_cpu(status_le);1560flags = brcmf_txstatus_get_field(status, FLAGS);1561hslot = brcmf_txstatus_get_field(status, HSLOT);1562genbit = brcmf_txstatus_get_field(status, GENERATION);1563if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {1564memcpy(&seq_le, &data[BRCMF_FWS_TYPE_TXSTATUS_LEN],1565sizeof(seq_le));1566seq = le16_to_cpu(seq_le);1567compcnt_offset += BRCMF_FWS_TYPE_SEQ_LEN;1568} else {1569seq = 0;1570}15711572if (type == BRCMF_FWS_TYPE_COMP_TXSTATUS)1573compcnt = data[compcnt_offset];1574else1575compcnt = 1;1576fws->stats.txs_indicate += compcnt;15771578brcmf_fws_lock(fws);1579brcmf_fws_txs_process(fws, flags, hslot, genbit, seq, compcnt);1580brcmf_fws_unlock(fws);1581return BRCMF_FWS_RET_OK_NOSCHEDULE;1582}15831584static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)1585{1586__le32 timestamp;15871588memcpy(×tamp, &data[2], sizeof(timestamp));1589brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],1590le32_to_cpu(timestamp));1591return 0;1592}15931594static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,1595const struct brcmf_event_msg *e,1596void *data)1597{1598struct brcmf_pub *drvr = ifp->drvr;1599struct brcmf_fws_info *fws = drvr_to_fws(drvr);1600int i;1601u8 *credits = data;16021603if (e->datalen < BRCMF_FWS_FIFO_COUNT) {1604bphy_err(drvr, "event payload too small (%d)\n", e->datalen);1605return -EINVAL;1606}16071608fws->creditmap_received = true;16091610brcmf_dbg(TRACE, "enter: credits %pM\n", credits);1611brcmf_fws_lock(fws);1612for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {1613fws->fifo_credit[i] += credits[i] - fws->init_fifo_credit[i];1614fws->init_fifo_credit[i] = credits[i];1615if (fws->fifo_credit[i] > 0)1616fws->fifo_credit_map |= 1 << i;1617else1618fws->fifo_credit_map &= ~(1 << i);1619WARN_ONCE(fws->fifo_credit[i] < 0,1620"fifo_credit[%d] is negative(%d)\n", i,1621fws->fifo_credit[i]);1622}1623brcmf_fws_schedule_deq(fws);1624brcmf_fws_unlock(fws);1625return 0;1626}16271628static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,1629const struct brcmf_event_msg *e,1630void *data)1631{1632struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);16331634if (fws) {1635brcmf_fws_lock(fws);1636fws->bcmc_credit_check = true;1637brcmf_fws_unlock(fws);1638}1639return 0;1640}16411642static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,1643u8 start, u8 end,1644struct sk_buff_head *skb_list)1645{1646/* initialize return list */1647__skb_queue_head_init(skb_list);16481649if (rfi->pend_pkts == 0) {1650brcmf_dbg(INFO, "no packets in reorder queue\n");1651return;1652}16531654do {1655if (rfi->pktslots[start]) {1656__skb_queue_tail(skb_list, rfi->pktslots[start]);1657rfi->pktslots[start] = NULL;1658}1659start++;1660if (start > rfi->max_idx)1661start = 0;1662} while (start != end);1663rfi->pend_pkts -= skb_queue_len(skb_list);1664}16651666void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt)1667{1668struct brcmf_pub *drvr = ifp->drvr;1669u8 *reorder_data;1670u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;1671struct brcmf_ampdu_rx_reorder *rfi;1672struct sk_buff_head reorder_list;1673struct sk_buff *pnext;1674u8 flags;16751676reorder_data = ((struct brcmf_skb_reorder_data *)pkt->cb)->reorder;1677flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];1678flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];16791680/* validate flags and flow id */1681if (flags == 0xFF) {1682bphy_err(drvr, "invalid flags...so ignore this packet\n");1683brcmf_netif_rx(ifp, pkt);1684return;1685}16861687rfi = ifp->drvr->reorder_flows[flow_id];1688if (flags & BRCMF_RXREORDER_DEL_FLOW) {1689brcmf_dbg(INFO, "flow-%d: delete\n",1690flow_id);16911692if (rfi == NULL) {1693brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",1694flow_id);1695brcmf_netif_rx(ifp, pkt);1696return;1697}16981699brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,1700&reorder_list);1701/* add the last packet */1702__skb_queue_tail(&reorder_list, pkt);1703kfree(rfi);1704ifp->drvr->reorder_flows[flow_id] = NULL;1705goto netif_rx;1706}1707/* from here on we need a flow reorder instance */1708if (rfi == NULL) {1709max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];17101711/* allocate space for flow reorder info */1712brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",1713flow_id, max_idx);1714rfi = kzalloc(struct_size(rfi, pktslots, max_idx + 1),1715GFP_ATOMIC);1716if (rfi == NULL) {1717bphy_err(drvr, "failed to alloc buffer\n");1718brcmf_netif_rx(ifp, pkt);1719return;1720}17211722ifp->drvr->reorder_flows[flow_id] = rfi;1723rfi->max_idx = max_idx;1724}1725if (flags & BRCMF_RXREORDER_NEW_HOLE) {1726if (rfi->pend_pkts) {1727brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,1728rfi->exp_idx,1729&reorder_list);1730WARN_ON(rfi->pend_pkts);1731} else {1732__skb_queue_head_init(&reorder_list);1733}1734rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];1735rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];1736rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];1737rfi->pktslots[rfi->cur_idx] = pkt;1738rfi->pend_pkts++;1739brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",1740flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);1741} else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {1742cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];1743exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];17441745if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {1746/* still in the current hole */1747/* enqueue the current on the buffer chain */1748if (rfi->pktslots[cur_idx] != NULL) {1749brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");1750brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);1751rfi->pktslots[cur_idx] = NULL;1752}1753rfi->pktslots[cur_idx] = pkt;1754rfi->pend_pkts++;1755rfi->cur_idx = cur_idx;1756brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",1757flow_id, cur_idx, exp_idx, rfi->pend_pkts);17581759/* can return now as there is no reorder1760* list to process.1761*/1762return;1763}1764if (rfi->exp_idx == cur_idx) {1765if (rfi->pktslots[cur_idx] != NULL) {1766brcmf_dbg(INFO, "error buffer pending..free it\n");1767brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);1768rfi->pktslots[cur_idx] = NULL;1769}1770rfi->pktslots[cur_idx] = pkt;1771rfi->pend_pkts++;17721773/* got the expected one. flush from current to expected1774* and update expected1775*/1776brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",1777flow_id, cur_idx, exp_idx, rfi->pend_pkts);17781779rfi->cur_idx = cur_idx;1780rfi->exp_idx = exp_idx;17811782brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,1783&reorder_list);1784brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",1785flow_id, skb_queue_len(&reorder_list),1786rfi->pend_pkts);1787} else {1788u8 end_idx;17891790brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",1791flow_id, flags, rfi->cur_idx, rfi->exp_idx,1792cur_idx, exp_idx);1793if (flags & BRCMF_RXREORDER_FLUSH_ALL)1794end_idx = rfi->exp_idx;1795else1796end_idx = exp_idx;17971798/* flush pkts first */1799brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,1800&reorder_list);18011802if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {1803__skb_queue_tail(&reorder_list, pkt);1804} else {1805rfi->pktslots[cur_idx] = pkt;1806rfi->pend_pkts++;1807}1808rfi->exp_idx = exp_idx;1809rfi->cur_idx = cur_idx;1810}1811} else {1812/* explicitly window move updating the expected index */1813exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];18141815brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",1816flow_id, flags, rfi->exp_idx, exp_idx);1817if (flags & BRCMF_RXREORDER_FLUSH_ALL)1818end_idx = rfi->exp_idx;1819else1820end_idx = exp_idx;18211822brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,1823&reorder_list);1824__skb_queue_tail(&reorder_list, pkt);1825/* set the new expected idx */1826rfi->exp_idx = exp_idx;1827}1828netif_rx:1829skb_queue_walk_safe(&reorder_list, pkt, pnext) {1830__skb_unlink(pkt, &reorder_list);1831brcmf_netif_rx(ifp, pkt);1832}1833}18341835void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)1836{1837struct brcmf_skb_reorder_data *rd;1838struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);1839u8 *signal_data;1840s16 data_len;1841u8 type;1842u8 len;1843u8 *data;1844s32 status;1845s32 err;18461847brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",1848ifp->ifidx, skb->len, siglen);18491850WARN_ON(siglen > skb->len);18511852if (siglen > skb->len)1853siglen = skb->len;18541855if (!siglen)1856return;1857/* if flow control disabled, skip to packet data and leave */1858if ((!fws) || (!fws->fw_signals)) {1859skb_pull(skb, siglen);1860return;1861}18621863fws->stats.header_pulls++;1864data_len = siglen;1865signal_data = skb->data;18661867status = BRCMF_FWS_RET_OK_NOSCHEDULE;1868while (data_len > 0) {1869/* extract tlv info */1870type = signal_data[0];18711872/* FILLER type is actually not a TLV, but1873* a single byte that can be skipped.1874*/1875if (type == BRCMF_FWS_TYPE_FILLER) {1876signal_data += 1;1877data_len -= 1;1878continue;1879}1880len = signal_data[1];1881data = signal_data + 2;18821883brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n",1884brcmf_fws_get_tlv_name(type), type, len,1885brcmf_fws_get_tlv_len(fws, type));18861887/* abort parsing when length invalid */1888if (data_len < len + 2)1889break;18901891if (len < brcmf_fws_get_tlv_len(fws, type))1892break;18931894err = BRCMF_FWS_RET_OK_NOSCHEDULE;1895switch (type) {1896case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:1897rd = (struct brcmf_skb_reorder_data *)skb->cb;1898rd->reorder = data;1899break;1900case BRCMF_FWS_TYPE_MACDESC_ADD:1901case BRCMF_FWS_TYPE_MACDESC_DEL:1902brcmf_fws_macdesc_indicate(fws, type, data);1903break;1904case BRCMF_FWS_TYPE_MAC_OPEN:1905case BRCMF_FWS_TYPE_MAC_CLOSE:1906err = brcmf_fws_macdesc_state_indicate(fws, type, data);1907break;1908case BRCMF_FWS_TYPE_INTERFACE_OPEN:1909case BRCMF_FWS_TYPE_INTERFACE_CLOSE:1910err = brcmf_fws_interface_state_indicate(fws, type,1911data);1912break;1913case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:1914case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:1915err = brcmf_fws_request_indicate(fws, type, data);1916break;1917case BRCMF_FWS_TYPE_TXSTATUS:1918case BRCMF_FWS_TYPE_COMP_TXSTATUS:1919brcmf_fws_txstatus_indicate(fws, type, data);1920break;1921case BRCMF_FWS_TYPE_FIFO_CREDITBACK:1922err = brcmf_fws_fifocreditback_indicate(fws, data);1923break;1924case BRCMF_FWS_TYPE_RSSI:1925brcmf_fws_rssi_indicate(fws, *data);1926break;1927case BRCMF_FWS_TYPE_TRANS_ID:1928brcmf_fws_dbg_seqnum_check(fws, data);1929break;1930case BRCMF_FWS_TYPE_PKTTAG:1931case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP:1932default:1933fws->stats.tlv_invalid_type++;1934break;1935}1936if (err == BRCMF_FWS_RET_OK_SCHEDULE)1937status = BRCMF_FWS_RET_OK_SCHEDULE;1938signal_data += len + 2;1939data_len -= len + 2;1940}19411942if (data_len != 0)1943fws->stats.tlv_parse_failed++;19441945if (status == BRCMF_FWS_RET_OK_SCHEDULE)1946brcmf_fws_schedule_deq(fws);19471948/* signalling processing result does1949* not affect the actual ethernet packet.1950*/1951skb_pull(skb, siglen);19521953/* this may be a signal-only packet1954*/1955if (skb->len == 0)1956fws->stats.header_only_pkt++;1957}19581959static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,1960struct sk_buff *p)1961{1962struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);1963struct brcmf_fws_mac_descriptor *entry = skcb->mac;1964u8 flags;19651966if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED)1967brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);1968flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;1969if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {1970/*1971* Indicate that this packet is being sent in response to an1972* explicit request from the firmware side.1973*/1974flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;1975}1976brcmf_skb_htod_tag_set_field(p, FLAGS, flags);1977return brcmf_fws_hdrpush(fws, p);1978}19791980static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,1981struct sk_buff *skb, int fifo)1982{1983struct brcmf_pub *drvr = fws->drvr;1984struct brcmf_fws_mac_descriptor *entry;1985struct sk_buff *pktout;1986int qidx, hslot;1987int rc = 0;19881989entry = brcmf_skbcb(skb)->mac;1990if (entry->occupied) {1991qidx = 2 * fifo;1992if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED)1993qidx++;19941995pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb);1996if (pktout == NULL) {1997bphy_err(drvr, "%s queue %d full\n", entry->name, qidx);1998rc = -ENOSPC;1999}2000} else {2001bphy_err(drvr, "%s entry removed\n", entry->name);2002rc = -ENOENT;2003}20042005if (rc) {2006fws->stats.rollback_failed++;2007hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);2008brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,2009hslot, 0, 0, 1);2010} else {2011fws->stats.rollback_success++;2012brcmf_fws_return_credits(fws, fifo, 1);2013brcmf_fws_macdesc_return_req_credit(skb);2014}2015}20162017static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws,2018int highest_lender_ac, int borrower_ac,2019bool borrow_all)2020{2021int lender_ac, borrow_limit = 0;20222023for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {20242025if (!borrow_all)2026borrow_limit =2027fws->init_fifo_credit[lender_ac] / BRCMF_BORROW_RATIO;2028else2029borrow_limit = 0;20302031if (fws->fifo_credit[lender_ac] > borrow_limit) {2032fws->credits_borrowed[borrower_ac][lender_ac]++;2033fws->fifo_credit[lender_ac]--;2034if (fws->fifo_credit[lender_ac] == 0)2035fws->fifo_credit_map &= ~(1 << lender_ac);2036fws->fifo_credit_map |= (1 << borrower_ac);2037brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac);2038return 0;2039}2040}2041fws->fifo_credit_map &= ~(1 << borrower_ac);2042return -ENAVAIL;2043}20442045static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,2046struct sk_buff *skb)2047{2048struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);2049struct brcmf_fws_mac_descriptor *entry;2050int rc;2051u8 ifidx;2052u8 data_offset;20532054entry = skcb->mac;2055if (IS_ERR(entry))2056return PTR_ERR(entry);20572058data_offset = brcmf_fws_precommit_skb(fws, fifo, skb);2059entry->transit_count++;2060if (entry->suppressed)2061entry->suppr_transit_count++;2062ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);2063brcmf_fws_unlock(fws);2064rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);2065brcmf_fws_lock(fws);2066brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,2067skcb->if_flags, skcb->htod, rc);2068if (rc < 0) {2069entry->transit_count--;2070if (entry->suppressed)2071entry->suppr_transit_count--;2072(void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);2073goto rollback;2074}20752076fws->stats.pkt2bus++;2077fws->stats.send_pkts[fifo]++;2078if (brcmf_skb_if_flags_get_field(skb, REQUESTED))2079fws->stats.requested_sent[fifo]++;20802081return rc;20822083rollback:2084brcmf_fws_rollback_toq(fws, skb, fifo);2085return rc;2086}20872088static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,2089int fifo)2090{2091struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);2092int rc, hslot;20932094skcb->htod = 0;2095skcb->htod_seq = 0;2096hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);2097brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);2098brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);2099brcmf_skb_htod_tag_set_field(p, FIFO, fifo);2100rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);2101if (!rc)2102skcb->mac->seq[fifo]++;2103else2104fws->stats.generic_error++;2105return rc;2106}21072108int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)2109{2110struct brcmf_pub *drvr = ifp->drvr;2111struct brcmf_fws_info *fws = drvr_to_fws(drvr);2112struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);2113struct ethhdr *eh = (struct ethhdr *)(skb->data);2114int fifo = BRCMF_FWS_FIFO_BCMC;2115bool multicast = is_multicast_ether_addr(eh->h_dest);2116int rc = 0;21172118brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));21192120/* set control buffer information */2121skcb->if_flags = 0;2122skcb->state = BRCMF_FWS_SKBSTATE_NEW;2123brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);21242125/* mapping from 802.1d priority to firmware fifo index */2126if (!multicast)2127fifo = brcmf_map_prio_to_aci(drvr->config, skb->priority);21282129brcmf_fws_lock(fws);2130if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)2131fws->borrow_defer_timestamp = jiffies +2132BRCMF_FWS_BORROW_DEFER_PERIOD;21332134skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);2135brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,2136eh->h_dest, multicast, fifo);2137if (!brcmf_fws_assign_htod(fws, skb, fifo)) {2138brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);2139brcmf_fws_schedule_deq(fws);2140} else {2141bphy_err(drvr, "no hanger slot available\n");2142rc = -ENOMEM;2143}2144brcmf_fws_unlock(fws);21452146return rc;2147}21482149void brcmf_fws_reset_interface(struct brcmf_if *ifp)2150{2151struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;21522153brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);2154if (!entry)2155return;21562157brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);2158}21592160void brcmf_fws_add_interface(struct brcmf_if *ifp)2161{2162struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);2163struct brcmf_fws_mac_descriptor *entry;21642165if (!ifp->ndev || !brcmf_fws_queue_skbs(fws))2166return;21672168entry = &fws->desc.iface[ifp->ifidx];2169ifp->fws_desc = entry;2170brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);2171brcmf_fws_macdesc_set_name(fws, entry);2172brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,2173BRCMF_FWS_PSQ_LEN);2174brcmf_dbg(TRACE, "added %s\n", entry->name);2175}21762177void brcmf_fws_del_interface(struct brcmf_if *ifp)2178{2179struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;2180struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);21812182if (!entry)2183return;21842185brcmf_fws_lock(fws);2186ifp->fws_desc = NULL;2187brcmf_dbg(TRACE, "deleting %s\n", entry->name);2188brcmf_fws_macdesc_cleanup(fws, &fws->desc.iface[ifp->ifidx],2189ifp->ifidx);2190brcmf_fws_macdesc_deinit(entry);2191brcmf_fws_cleanup(fws, ifp->ifidx);2192brcmf_fws_unlock(fws);2193}21942195static void brcmf_fws_dequeue_worker(struct work_struct *worker)2196{2197struct brcmf_fws_info *fws;2198struct brcmf_pub *drvr;2199struct sk_buff *skb;2200int fifo;2201u32 hslot;2202u32 ifidx;2203int ret;22042205fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);2206drvr = fws->drvr;22072208brcmf_fws_lock(fws);2209for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;2210fifo--) {2211if (!brcmf_fws_fc_active(fws)) {2212while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {2213hslot = brcmf_skb_htod_tag_get_field(skb,2214HSLOT);2215brcmf_fws_hanger_poppkt(&fws->hanger, hslot,2216&skb, true);2217ifidx = brcmf_skb_if_flags_get_field(skb,2218INDEX);2219/* Use proto layer to send data frame */2220brcmf_fws_unlock(fws);2221ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);2222brcmf_fws_lock(fws);2223if (ret < 0)2224brcmf_txfinalize(brcmf_get_ifp(drvr,2225ifidx),2226skb, false);2227if (fws->bus_flow_blocked)2228break;2229}2230continue;2231}22322233while ((fws->fifo_credit[fifo]) ||2234((!fws->bcmc_credit_check) &&2235(fifo == BRCMF_FWS_FIFO_BCMC))) {2236skb = brcmf_fws_deq(fws, fifo);2237if (!skb)2238break;2239fws->fifo_credit[fifo]--;2240if (brcmf_fws_commit_skb(fws, fifo, skb))2241break;2242if (fws->bus_flow_blocked)2243break;2244}22452246if (fifo >= BRCMF_FWS_FIFO_AC_BE &&2247fifo <= BRCMF_FWS_FIFO_AC_VO &&2248fws->fifo_credit[fifo] == 0 &&2249!fws->bus_flow_blocked) {2250while (brcmf_fws_borrow_credit(fws,2251fifo - 1, fifo,2252true) == 0) {2253skb = brcmf_fws_deq(fws, fifo);2254if (!skb) {2255brcmf_fws_return_credits(fws, fifo, 1);2256break;2257}2258if (brcmf_fws_commit_skb(fws, fifo, skb))2259break;2260if (fws->bus_flow_blocked)2261break;2262}2263}2264}2265brcmf_fws_unlock(fws);2266}22672268#ifdef DEBUG2269static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)2270{2271struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);2272struct brcmf_fws_stats *fwstats = &(drvr_to_fws(bus_if->drvr)->stats);22732274seq_printf(seq,2275"header_pulls: %u\n"2276"header_only_pkt: %u\n"2277"tlv_parse_failed: %u\n"2278"tlv_invalid_type: %u\n"2279"mac_update_fails: %u\n"2280"ps_update_fails: %u\n"2281"if_update_fails: %u\n"2282"pkt2bus: %u\n"2283"generic_error: %u\n"2284"rollback_success: %u\n"2285"rollback_failed: %u\n"2286"delayq_full: %u\n"2287"supprq_full: %u\n"2288"txs_indicate: %u\n"2289"txs_discard: %u\n"2290"txs_suppr_core: %u\n"2291"txs_suppr_ps: %u\n"2292"txs_tossed: %u\n"2293"txs_host_tossed: %u\n"2294"bus_flow_block: %u\n"2295"fws_flow_block: %u\n"2296"send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"2297"requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",2298fwstats->header_pulls,2299fwstats->header_only_pkt,2300fwstats->tlv_parse_failed,2301fwstats->tlv_invalid_type,2302fwstats->mac_update_failed,2303fwstats->mac_ps_update_failed,2304fwstats->if_update_failed,2305fwstats->pkt2bus,2306fwstats->generic_error,2307fwstats->rollback_success,2308fwstats->rollback_failed,2309fwstats->delayq_full_error,2310fwstats->supprq_full_error,2311fwstats->txs_indicate,2312fwstats->txs_discard,2313fwstats->txs_supp_core,2314fwstats->txs_supp_ps,2315fwstats->txs_tossed,2316fwstats->txs_host_tossed,2317fwstats->bus_flow_block,2318fwstats->fws_flow_block,2319fwstats->send_pkts[0], fwstats->send_pkts[1],2320fwstats->send_pkts[2], fwstats->send_pkts[3],2321fwstats->send_pkts[4],2322fwstats->requested_sent[0],2323fwstats->requested_sent[1],2324fwstats->requested_sent[2],2325fwstats->requested_sent[3],2326fwstats->requested_sent[4]);23272328return 0;2329}2330#else2331static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)2332{2333return 0;2334}2335#endif23362337struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr)2338{2339struct brcmf_fws_info *fws;2340struct brcmf_if *ifp;2341u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;2342int rc;2343u32 mode;23442345fws = kzalloc(sizeof(*fws), GFP_KERNEL);2346if (!fws) {2347rc = -ENOMEM;2348goto fail;2349}23502351spin_lock_init(&fws->spinlock);23522353/* store drvr reference */2354fws->drvr = drvr;2355fws->fcmode = drvr->settings->fcmode;23562357if (!drvr->bus_if->always_use_fws_queue &&2358(fws->fcmode == BRCMF_FWS_FCMODE_NONE)) {2359fws->avoid_queueing = true;2360brcmf_dbg(INFO, "FWS queueing will be avoided\n");2361return fws;2362}23632364fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");2365if (fws->fws_wq == NULL) {2366bphy_err(drvr, "workqueue creation failed\n");2367rc = -EBADF;2368goto fail;2369}2370INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);23712372/* enable firmware signalling if fcmode active */2373if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)2374tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |2375BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |2376BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |2377BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;23782379rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,2380brcmf_fws_notify_credit_map);2381if (rc < 0) {2382bphy_err(drvr, "register credit map handler failed\n");2383goto fail;2384}2385rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT,2386brcmf_fws_notify_bcmc_credit_support);2387if (rc < 0) {2388bphy_err(drvr, "register bcmc credit handler failed\n");2389brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);2390goto fail;2391}23922393/* Setting the iovar may fail if feature is unsupported2394* so leave the rc as is so driver initialization can2395* continue. Set mode back to none indicating not enabled.2396*/2397fws->fw_signals = true;2398ifp = brcmf_get_ifp(drvr, 0);2399if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {2400bphy_err(drvr, "failed to set bdcv2 tlv signaling\n");2401fws->fcmode = BRCMF_FWS_FCMODE_NONE;2402fws->fw_signals = false;2403}24042405if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))2406brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");24072408/* Enable seq number reuse, if supported */2409if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {2410if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {2411mode = 0;2412BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);2413if (brcmf_fil_iovar_int_set(ifp,2414"wlfc_mode", mode) == 0) {2415BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);2416}2417}2418}24192420brcmf_fws_hanger_init(&fws->hanger);2421brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);2422brcmf_fws_macdesc_set_name(fws, &fws->desc.other);2423brcmf_dbg(INFO, "added %s\n", fws->desc.other.name);2424brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,2425BRCMF_FWS_PSQ_LEN);24262427brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",2428fws->fw_signals ? "enabled" : "disabled", tlv);2429return fws;24302431fail:2432brcmf_fws_detach(fws);2433return ERR_PTR(rc);2434}24352436void brcmf_fws_detach(struct brcmf_fws_info *fws)2437{2438if (!fws)2439return;24402441if (fws->fws_wq)2442destroy_workqueue(fws->fws_wq);24432444/* cleanup */2445brcmf_fws_lock(fws);2446brcmf_fws_cleanup(fws, -1);2447brcmf_fws_unlock(fws);24482449/* free top structure */2450kfree(fws);2451}24522453void brcmf_fws_debugfs_create(struct brcmf_pub *drvr)2454{2455/* create debugfs file for statistics */2456brcmf_debugfs_add_entry(drvr, "fws_stats",2457brcmf_debugfs_fws_stats_read);2458}24592460bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws)2461{2462return !fws->avoid_queueing;2463}24642465bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)2466{2467if (!fws->creditmap_received)2468return false;24692470return fws->fcmode != BRCMF_FWS_FCMODE_NONE;2471}24722473void brcmf_fws_bustxcomplete(struct brcmf_fws_info *fws, struct sk_buff *skb,2474bool success)2475{2476u32 hslot;24772478if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {2479brcmu_pkt_buf_free_skb(skb);2480return;2481}24822483if (!success) {2484brcmf_fws_lock(fws);2485hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);2486brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot,24870, 0, 1);2488brcmf_fws_unlock(fws);2489}2490}24912492void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)2493{2494struct brcmf_fws_info *fws = drvr_to_fws(drvr);2495struct brcmf_if *ifp;2496int i;24972498if (fws->avoid_queueing) {2499for (i = 0; i < BRCMF_MAX_IFS; i++) {2500ifp = drvr->iflist[i];2501if (!ifp || !ifp->ndev)2502continue;2503brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW,2504flow_blocked);2505}2506} else {2507fws->bus_flow_blocked = flow_blocked;2508if (!flow_blocked)2509brcmf_fws_schedule_deq(fws);2510else2511fws->stats.bus_flow_block++;2512}2513}251425152516