1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include <linux/types.h>
17#include <linux/module.h>
18#include <linux/if_ether.h>
19#include <linux/spinlock.h>
20#include <linux/skbuff.h>
21#include <linux/netdevice.h>
22#include <linux/etherdevice.h>
23#include <linux/err.h>
24#include <linux/jiffies.h>
25#include <net/cfg80211.h>
26
27#include <brcmu_utils.h>
28#include <brcmu_wifi.h>
29#include "core.h"
30#include "debug.h"
31#include "bus.h"
32#include "fwil.h"
33#include "fwil_types.h"
34#include "fweh.h"
35#include "fwsignal.h"
36#include "p2p.h"
37#include "cfg80211.h"
38#include "proto.h"
39#include "bcdc.h"
40#include "common.h"
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56#define BRCMF_FWS_TLV_DEFLIST \
57 BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \
58 BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \
59 BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \
60 BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \
61 BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \
62 BRCMF_FWS_TLV_DEF(MACDESC_ADD, 6, 8) \
63 BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \
64 BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \
65 BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \
66 BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \
67 BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \
68 BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \
69 BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \
70 BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \
71 BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \
72 BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \
73 BRCMF_FWS_TLV_DEF(FILLER, 255, 0)
74
75
76
77
78#define BRCMF_FWS_TLV_DEF(name, id, len) \
79 BRCMF_FWS_TYPE_ ## name = id,
80enum brcmf_fws_tlv_type {
81 BRCMF_FWS_TLV_DEFLIST
82 BRCMF_FWS_TYPE_INVALID
83};
84#undef BRCMF_FWS_TLV_DEF
85
86
87
88
89#define BRCMF_FWS_TLV_DEF(name, id, len) \
90 BRCMF_FWS_TYPE_ ## name ## _LEN = (len),
91enum brcmf_fws_tlv_len {
92 BRCMF_FWS_TLV_DEFLIST
93};
94#undef BRCMF_FWS_TLV_DEF
95
96
97#define BRCMF_RXREORDER_FLOWID_OFFSET 0
98#define BRCMF_RXREORDER_MAXIDX_OFFSET 2
99#define BRCMF_RXREORDER_FLAGS_OFFSET 4
100#define BRCMF_RXREORDER_CURIDX_OFFSET 6
101#define BRCMF_RXREORDER_EXPIDX_OFFSET 8
102
103#define BRCMF_RXREORDER_DEL_FLOW 0x01
104#define BRCMF_RXREORDER_FLUSH_ALL 0x02
105#define BRCMF_RXREORDER_CURIDX_VALID 0x04
106#define BRCMF_RXREORDER_EXPIDX_VALID 0x08
107#define BRCMF_RXREORDER_NEW_HOLE 0x10
108
109#ifdef DEBUG
110
111
112
113#define BRCMF_FWS_TLV_DEF(name, id, len) \
114 { id, #name },
115static struct {
116 enum brcmf_fws_tlv_type id;
117 const char *name;
118} brcmf_fws_tlv_names[] = {
119 BRCMF_FWS_TLV_DEFLIST
120};
121#undef BRCMF_FWS_TLV_DEF
122
123
124static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
125{
126 int i;
127
128 for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++)
129 if (brcmf_fws_tlv_names[i].id == id)
130 return brcmf_fws_tlv_names[i].name;
131
132 return "INVALID";
133}
134#else
135static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
136{
137 return "NODEBUG";
138}
139#endif
140
141
142
143
144
145#define BRCMF_FWS_TYPE_SEQ_LEN 2
146
147
148
149
150#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001
151#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002
152#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004
153#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008
154#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010
155#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020
156#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040
157
158#define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32
159#define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff
160
161#define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0
162#define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1
163#define BRCMF_FWS_FLOWCONTROL_HIWATER 128
164#define BRCMF_FWS_FLOWCONTROL_LOWATER 64
165
166#define BRCMF_FWS_PSQ_PREC_COUNT ((BRCMF_FWS_FIFO_COUNT + 1) * 2)
167#define BRCMF_FWS_PSQ_LEN 256
168
169#define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01
170#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02
171
172#define BRCMF_FWS_RET_OK_NOSCHEDULE 0
173#define BRCMF_FWS_RET_OK_SCHEDULE 1
174
175#define BRCMF_FWS_MODE_REUSESEQ_SHIFT 3
176#define BRCMF_FWS_MODE_SET_REUSESEQ(x, val) ((x) = \
177 ((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \
178 (((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT))
179#define BRCMF_FWS_MODE_GET_REUSESEQ(x) \
180 (((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1)
181
182
183
184
185
186
187
188
189
190enum brcmf_fws_skb_state {
191 BRCMF_FWS_SKBSTATE_NEW,
192 BRCMF_FWS_SKBSTATE_DELAYED,
193 BRCMF_FWS_SKBSTATE_SUPPRESSED,
194 BRCMF_FWS_SKBSTATE_TIM
195};
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210struct brcmf_skbuff_cb {
211 u16 bus_flags;
212 u16 if_flags;
213 u32 htod;
214 u16 htod_seq;
215 enum brcmf_fws_skb_state state;
216 struct brcmf_fws_mac_descriptor *mac;
217};
218
219
220
221
222#define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb))
223
224
225
226
227
228
229
230
231
232
233
234#define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800
235#define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11
236#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400
237#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT 10
238#define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK 0x0200
239#define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT 9
240#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK 0x0100
241#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT 8
242#define BRCMF_SKB_IF_FLAGS_IF_AP_MASK 0x0080
243#define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT 7
244#define BRCMF_SKB_IF_FLAGS_INDEX_MASK 0x000f
245#define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0
246
247#define brcmf_skb_if_flags_set_field(skb, field, value) \
248 brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \
249 BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
250 BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value))
251#define brcmf_skb_if_flags_get_field(skb, field) \
252 brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \
253 BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
254 BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT)
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271#define BRCMF_SKB_HTOD_TAG_GENERATION_MASK 0x80000000
272#define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT 31
273#define BRCMF_SKB_HTOD_TAG_FLAGS_MASK 0x78000000
274#define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT 27
275#define BRCMF_SKB_HTOD_TAG_FIFO_MASK 0x07000000
276#define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT 24
277#define BRCMF_SKB_HTOD_TAG_HSLOT_MASK 0x00ffff00
278#define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT 8
279#define BRCMF_SKB_HTOD_TAG_FREERUN_MASK 0x000000ff
280#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0
281
282#define brcmf_skb_htod_tag_set_field(skb, field, value) \
283 brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \
284 BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
285 BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value))
286#define brcmf_skb_htod_tag_get_field(skb, field) \
287 brcmu_maskget32(brcmf_skbcb(skb)->htod, \
288 BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
289 BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT)
290
291#define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK 0x2000
292#define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT 13
293#define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK 0x1000
294#define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT 12
295#define BRCMF_SKB_HTOD_SEQ_NR_MASK 0x0fff
296#define BRCMF_SKB_HTOD_SEQ_NR_SHIFT 0
297
298#define brcmf_skb_htod_seq_set_field(skb, field, value) \
299 brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \
300 BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
301 BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value))
302#define brcmf_skb_htod_seq_get_field(skb, field) \
303 brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \
304 BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
305 BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT)
306
307#define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000
308#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31
309#define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000
310#define BRCMF_FWS_TXSTAT_FLAGS_SHIFT 27
311#define BRCMF_FWS_TXSTAT_FIFO_MASK 0x07000000
312#define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24
313#define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00
314#define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8
315#define BRCMF_FWS_TXSTAT_FREERUN_MASK 0x000000FF
316#define BRCMF_FWS_TXSTAT_FREERUN_SHIFT 0
317
318#define brcmf_txstatus_get_field(txs, field) \
319 brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
320 BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)
321
322
323#define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10)
324
325
326
327
328
329
330
331
332
333
334
335
336
337enum brcmf_fws_fifo {
338 BRCMF_FWS_FIFO_FIRST,
339 BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST,
340 BRCMF_FWS_FIFO_AC_BE,
341 BRCMF_FWS_FIFO_AC_VI,
342 BRCMF_FWS_FIFO_AC_VO,
343 BRCMF_FWS_FIFO_BCMC,
344 BRCMF_FWS_FIFO_ATIM,
345 BRCMF_FWS_FIFO_COUNT
346};
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362enum brcmf_fws_txstatus {
363 BRCMF_FWS_TXSTATUS_DISCARD,
364 BRCMF_FWS_TXSTATUS_CORE_SUPPRESS,
365 BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS,
366 BRCMF_FWS_TXSTATUS_FW_TOSSED,
367 BRCMF_FWS_TXSTATUS_HOST_TOSSED
368};
369
370enum brcmf_fws_fcmode {
371 BRCMF_FWS_FCMODE_NONE,
372 BRCMF_FWS_FCMODE_IMPLIED_CREDIT,
373 BRCMF_FWS_FCMODE_EXPLICIT_CREDIT
374};
375
376enum brcmf_fws_mac_desc_state {
377 BRCMF_FWS_STATE_OPEN = 1,
378 BRCMF_FWS_STATE_CLOSE
379};
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397struct brcmf_fws_mac_descriptor {
398 char name[16];
399 u8 occupied;
400 u8 mac_handle;
401 u8 interface_id;
402 u8 state;
403 bool suppressed;
404 u8 generation;
405 u8 ac_bitmap;
406 u8 requested_credit;
407 u8 requested_packet;
408 u8 ea[ETH_ALEN];
409 u8 seq[BRCMF_FWS_FIFO_COUNT];
410 struct pktq psq;
411 int transit_count;
412 int suppr_transit_count;
413 bool send_tim_signal;
414 u8 traffic_pending_bmp;
415 u8 traffic_lastreported_bmp;
416};
417
418#define BRCMF_FWS_HANGER_MAXITEMS 1024
419
420
421
422
423
424
425
426
427enum brcmf_fws_hanger_item_state {
428 BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1,
429 BRCMF_FWS_HANGER_ITEM_STATE_INUSE,
430 BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED
431};
432
433
434
435
436
437
438
439
440struct brcmf_fws_hanger_item {
441 enum brcmf_fws_hanger_item_state state;
442 struct sk_buff *pkt;
443};
444
445
446
447
448
449
450
451
452
453
454
455
456struct brcmf_fws_hanger {
457 u32 pushed;
458 u32 popped;
459 u32 failed_to_push;
460 u32 failed_to_pop;
461 u32 failed_slotfind;
462 u32 slot_pos;
463 struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS];
464};
465
466struct brcmf_fws_macdesc_table {
467 struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE];
468 struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS];
469 struct brcmf_fws_mac_descriptor other;
470};
471
472struct brcmf_fws_stats {
473 u32 tlv_parse_failed;
474 u32 tlv_invalid_type;
475 u32 header_only_pkt;
476 u32 header_pulls;
477 u32 pkt2bus;
478 u32 send_pkts[5];
479 u32 requested_sent[5];
480 u32 generic_error;
481 u32 mac_update_failed;
482 u32 mac_ps_update_failed;
483 u32 if_update_failed;
484 u32 packet_request_failed;
485 u32 credit_request_failed;
486 u32 rollback_success;
487 u32 rollback_failed;
488 u32 delayq_full_error;
489 u32 supprq_full_error;
490 u32 txs_indicate;
491 u32 txs_discard;
492 u32 txs_supp_core;
493 u32 txs_supp_ps;
494 u32 txs_tossed;
495 u32 txs_host_tossed;
496 u32 bus_flow_block;
497 u32 fws_flow_block;
498};
499
500struct brcmf_fws_info {
501 struct brcmf_pub *drvr;
502 spinlock_t spinlock;
503 ulong flags;
504 struct brcmf_fws_stats stats;
505 struct brcmf_fws_hanger hanger;
506 enum brcmf_fws_fcmode fcmode;
507 bool fw_signals;
508 bool bcmc_credit_check;
509 struct brcmf_fws_macdesc_table desc;
510 struct workqueue_struct *fws_wq;
511 struct work_struct fws_dequeue_work;
512 u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
513 int fifo_credit[BRCMF_FWS_FIFO_COUNT];
514 int init_fifo_credit[BRCMF_FWS_FIFO_COUNT];
515 int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1];
516 int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
517 u32 fifo_credit_map;
518 u32 fifo_delay_map;
519 unsigned long borrow_defer_timestamp;
520 bool bus_flow_blocked;
521 bool creditmap_received;
522 u8 mode;
523 bool avoid_queueing;
524};
525
526
527
528
529static const int brcmf_fws_prio2fifo[] = {
530 BRCMF_FWS_FIFO_AC_BE,
531 BRCMF_FWS_FIFO_AC_BK,
532 BRCMF_FWS_FIFO_AC_BK,
533 BRCMF_FWS_FIFO_AC_BE,
534 BRCMF_FWS_FIFO_AC_VI,
535 BRCMF_FWS_FIFO_AC_VI,
536 BRCMF_FWS_FIFO_AC_VO,
537 BRCMF_FWS_FIFO_AC_VO
538};
539
540#define BRCMF_FWS_TLV_DEF(name, id, len) \
541 case BRCMF_FWS_TYPE_ ## name: \
542 return len;
543
544
545
546
547
548
549
550
551
552static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
553 enum brcmf_fws_tlv_type id)
554{
555 switch (id) {
556 BRCMF_FWS_TLV_DEFLIST
557 default:
558 fws->stats.tlv_invalid_type++;
559 break;
560 }
561 return -EINVAL;
562}
563#undef BRCMF_FWS_TLV_DEF
564
565static void brcmf_fws_lock(struct brcmf_fws_info *fws)
566 __acquires(&fws->spinlock)
567{
568 spin_lock_irqsave(&fws->spinlock, fws->flags);
569}
570
571static void brcmf_fws_unlock(struct brcmf_fws_info *fws)
572 __releases(&fws->spinlock)
573{
574 spin_unlock_irqrestore(&fws->spinlock, fws->flags);
575}
576
577static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
578{
579 u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
580 return ifidx == *(int *)arg;
581}
582
583static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)
584{
585 int i;
586
587 memset(hanger, 0, sizeof(*hanger));
588 for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
589 hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
590}
591
592static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
593{
594 u32 i;
595
596 i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
597
598 while (i != h->slot_pos) {
599 if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
600 h->slot_pos = i;
601 goto done;
602 }
603 i++;
604 if (i == BRCMF_FWS_HANGER_MAXITEMS)
605 i = 0;
606 }
607 brcmf_err("all slots occupied\n");
608 h->failed_slotfind++;
609 i = BRCMF_FWS_HANGER_MAXITEMS;
610done:
611 return i;
612}
613
614static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
615 struct sk_buff *pkt, u32 slot_id)
616{
617 if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
618 return -ENOENT;
619
620 if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
621 brcmf_err("slot is not free\n");
622 h->failed_to_push++;
623 return -EINVAL;
624 }
625
626 h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE;
627 h->items[slot_id].pkt = pkt;
628 h->pushed++;
629 return 0;
630}
631
632static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
633 u32 slot_id, struct sk_buff **pktout,
634 bool remove_item)
635{
636 if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
637 return -ENOENT;
638
639 if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
640 brcmf_err("entry not in use\n");
641 h->failed_to_pop++;
642 return -EINVAL;
643 }
644
645 *pktout = h->items[slot_id].pkt;
646 if (remove_item) {
647 h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
648 h->items[slot_id].pkt = NULL;
649 h->popped++;
650 }
651 return 0;
652}
653
654static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q,
655 int ifidx)
656{
657 bool (*matchfn)(struct sk_buff *, void *) = NULL;
658 struct sk_buff *skb;
659 int prec;
660 u32 hslot;
661
662 if (ifidx != -1)
663 matchfn = brcmf_fws_ifidx_match;
664 for (prec = 0; prec < q->num_prec; prec++) {
665 skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
666 while (skb) {
667 hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
668 brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
669 true);
670 brcmu_pkt_buf_free_skb(skb);
671 skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
672 }
673 }
674}
675
676static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
677 u32 slot_id)
678{
679 if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
680 return -ENOENT;
681
682 if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
683 brcmf_err("entry not in use\n");
684 return -EINVAL;
685 }
686
687 h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
688 return 0;
689}
690
691static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
692 bool (*fn)(struct sk_buff *, void *),
693 int ifidx)
694{
695 struct brcmf_fws_hanger *h = &fws->hanger;
696 struct sk_buff *skb;
697 int i;
698 enum brcmf_fws_hanger_item_state s;
699
700 for (i = 0; i < ARRAY_SIZE(h->items); i++) {
701 s = h->items[i].state;
702 if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
703 s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
704 skb = h->items[i].pkt;
705 if (fn == NULL || fn(skb, &ifidx)) {
706
707 if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE)
708 brcmu_pkt_buf_free_skb(skb);
709 h->items[i].state =
710 BRCMF_FWS_HANGER_ITEM_STATE_FREE;
711 }
712 }
713 }
714}
715
716static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws,
717 struct brcmf_fws_mac_descriptor *desc)
718{
719 if (desc == &fws->desc.other)
720 strlcpy(desc->name, "MAC-OTHER", sizeof(desc->name));
721 else if (desc->mac_handle)
722 scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d",
723 desc->mac_handle, desc->interface_id);
724 else
725 scnprintf(desc->name, sizeof(desc->name), "MACIF:%d",
726 desc->interface_id);
727}
728
729static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc,
730 u8 *addr, u8 ifidx)
731{
732 brcmf_dbg(TRACE,
733 "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx);
734 desc->occupied = 1;
735 desc->state = BRCMF_FWS_STATE_OPEN;
736 desc->requested_credit = 0;
737 desc->requested_packet = 0;
738
739 desc->interface_id = ifidx;
740 desc->ac_bitmap = 0xff;
741 if (addr)
742 memcpy(&desc->ea[0], addr, ETH_ALEN);
743}
744
745static
746void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc)
747{
748 brcmf_dbg(TRACE,
749 "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id);
750 desc->occupied = 0;
751 desc->state = BRCMF_FWS_STATE_CLOSE;
752 desc->requested_credit = 0;
753 desc->requested_packet = 0;
754}
755
756static struct brcmf_fws_mac_descriptor *
757brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)
758{
759 struct brcmf_fws_mac_descriptor *entry;
760 int i;
761
762 if (ea == NULL)
763 return ERR_PTR(-EINVAL);
764
765 entry = &fws->desc.nodes[0];
766 for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) {
767 if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN))
768 return entry;
769 entry++;
770 }
771
772 return ERR_PTR(-ENOENT);
773}
774
775static struct brcmf_fws_mac_descriptor*
776brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da)
777{
778 struct brcmf_fws_mac_descriptor *entry = &fws->desc.other;
779 bool multicast;
780
781 multicast = is_multicast_ether_addr(da);
782
783
784
785
786
787 if (multicast && ifp->fws_desc) {
788 entry = ifp->fws_desc;
789 goto done;
790 }
791
792 entry = brcmf_fws_macdesc_lookup(fws, da);
793 if (IS_ERR(entry))
794 entry = ifp->fws_desc;
795
796done:
797 return entry;
798}
799
800static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws,
801 struct brcmf_fws_mac_descriptor *entry,
802 int fifo)
803{
804 struct brcmf_fws_mac_descriptor *if_entry;
805 bool closed;
806
807
808
809
810 if (entry->mac_handle) {
811 if_entry = &fws->desc.iface[entry->interface_id];
812 if (if_entry->state == BRCMF_FWS_STATE_CLOSE)
813 return true;
814 }
815
816
817
818 closed = entry->state == BRCMF_FWS_STATE_CLOSE &&
819 !entry->requested_credit && !entry->requested_packet;
820
821
822 return closed || !(entry->ac_bitmap & BIT(fifo));
823}
824
825static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,
826 struct brcmf_fws_mac_descriptor *entry,
827 int ifidx)
828{
829 if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
830 brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
831 entry->occupied = !!(entry->psq.len);
832 }
833}
834
835static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,
836 bool (*fn)(struct sk_buff *, void *),
837 int ifidx)
838{
839 struct brcmf_fws_hanger_item *hi;
840 struct pktq *txq;
841 struct sk_buff *skb;
842 int prec;
843 u32 hslot;
844
845 txq = brcmf_bus_gettxq(fws->drvr->bus_if);
846 if (IS_ERR(txq)) {
847 brcmf_dbg(TRACE, "no txq to clean up\n");
848 return;
849 }
850
851 for (prec = 0; prec < txq->num_prec; prec++) {
852 skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
853 while (skb) {
854 hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
855 hi = &fws->hanger.items[hslot];
856 WARN_ON(skb != hi->pkt);
857 hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
858 brcmu_pkt_buf_free_skb(skb);
859 skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
860 }
861 }
862}
863
864static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
865{
866 int i;
867 struct brcmf_fws_mac_descriptor *table;
868 bool (*matchfn)(struct sk_buff *, void *) = NULL;
869
870 if (fws == NULL)
871 return;
872
873 if (ifidx != -1)
874 matchfn = brcmf_fws_ifidx_match;
875
876
877 table = &fws->desc.nodes[0];
878 for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)
879 brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx);
880
881 brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx);
882 brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx);
883 brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
884}
885
886static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
887{
888 struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
889 u8 *wlh;
890 u16 data_offset = 0;
891 u8 fillers;
892 __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
893 __le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq);
894
895 brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n",
896 entry->name, brcmf_skb_if_flags_get_field(skb, INDEX),
897 (le32_to_cpu(pkttag) >> 8) & 0xffff,
898 brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq);
899 if (entry->send_tim_signal)
900 data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
901 if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
902 data_offset += BRCMF_FWS_TYPE_SEQ_LEN;
903
904 data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
905 fillers = round_up(data_offset, 4) - data_offset;
906 data_offset += fillers;
907
908 skb_push(skb, data_offset);
909 wlh = skb->data;
910
911 wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
912 wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
913 memcpy(&wlh[2], &pkttag, sizeof(pkttag));
914 if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
915 wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN;
916 memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq,
917 sizeof(pktseq));
918 }
919 wlh += wlh[1] + 2;
920
921 if (entry->send_tim_signal) {
922 entry->send_tim_signal = 0;
923 wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
924 wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
925 wlh[2] = entry->mac_handle;
926 wlh[3] = entry->traffic_pending_bmp;
927 brcmf_dbg(TRACE, "adding TIM info: handle %d bmp 0x%X\n",
928 entry->mac_handle, entry->traffic_pending_bmp);
929 wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
930 entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
931 }
932 if (fillers)
933 memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
934
935 return (u8)(data_offset >> 2);
936}
937
938static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
939 struct brcmf_fws_mac_descriptor *entry,
940 int fifo, bool send_immediately)
941{
942 struct sk_buff *skb;
943 struct brcmf_skbuff_cb *skcb;
944 s32 err;
945 u32 len;
946 u8 data_offset;
947 int ifidx;
948
949
950 if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
951 entry->traffic_pending_bmp &= ~NBITVAL(fifo);
952 else
953 entry->traffic_pending_bmp |= NBITVAL(fifo);
954
955 entry->send_tim_signal = false;
956 if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)
957 entry->send_tim_signal = true;
958 if (send_immediately && entry->send_tim_signal &&
959 entry->state == BRCMF_FWS_STATE_CLOSE) {
960
961
962 len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
963 BRCMF_FWS_TYPE_SEQ_LEN +
964 BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
965 4 + fws->drvr->hdrlen;
966 skb = brcmu_pkt_buf_get_skb(len);
967 if (skb == NULL)
968 return false;
969 skb_pull(skb, len);
970 skcb = brcmf_skbcb(skb);
971 skcb->mac = entry;
972 skcb->state = BRCMF_FWS_SKBSTATE_TIM;
973 skcb->htod = 0;
974 skcb->htod_seq = 0;
975 data_offset = brcmf_fws_hdrpush(fws, skb);
976 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
977 brcmf_fws_unlock(fws);
978 err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
979 brcmf_fws_lock(fws);
980 if (err)
981 brcmu_pkt_buf_free_skb(skb);
982 return true;
983 }
984 return false;
985}
986
987static void
988brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
989 u8 if_id)
990{
991 struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
992
993 if (WARN_ON(!ifp))
994 return;
995
996 if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
997 pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER)
998 brcmf_txflowblock_if(ifp,
999 BRCMF_NETIF_STOP_REASON_FWS_FC, false);
1000 if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
1001 pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) {
1002 fws->stats.fws_flow_block++;
1003 brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);
1004 }
1005 return;
1006}
1007
1008static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
1009{
1010 brcmf_dbg(CTL, "rssi %d\n", rssi);
1011 return 0;
1012}
1013
1014static
1015int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
1016{
1017 struct brcmf_fws_mac_descriptor *entry, *existing;
1018 u8 mac_handle;
1019 u8 ifidx;
1020 u8 *addr;
1021
1022 mac_handle = *data++;
1023 ifidx = *data++;
1024 addr = data;
1025
1026 entry = &fws->desc.nodes[mac_handle & 0x1F];
1027 if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
1028 if (entry->occupied) {
1029 brcmf_dbg(TRACE, "deleting %s mac %pM\n",
1030 entry->name, addr);
1031 brcmf_fws_lock(fws);
1032 brcmf_fws_macdesc_cleanup(fws, entry, -1);
1033 brcmf_fws_macdesc_deinit(entry);
1034 brcmf_fws_unlock(fws);
1035 } else
1036 fws->stats.mac_update_failed++;
1037 return 0;
1038 }
1039
1040 existing = brcmf_fws_macdesc_lookup(fws, addr);
1041 if (IS_ERR(existing)) {
1042 if (!entry->occupied) {
1043 brcmf_fws_lock(fws);
1044 entry->mac_handle = mac_handle;
1045 brcmf_fws_macdesc_init(entry, addr, ifidx);
1046 brcmf_fws_macdesc_set_name(fws, entry);
1047 brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
1048 BRCMF_FWS_PSQ_LEN);
1049 brcmf_fws_unlock(fws);
1050 brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
1051 } else {
1052 fws->stats.mac_update_failed++;
1053 }
1054 } else {
1055 if (entry != existing) {
1056 brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
1057 brcmf_fws_lock(fws);
1058 memcpy(entry, existing,
1059 offsetof(struct brcmf_fws_mac_descriptor, psq));
1060 entry->mac_handle = mac_handle;
1061 brcmf_fws_macdesc_deinit(existing);
1062 brcmf_fws_macdesc_set_name(fws, entry);
1063 brcmf_fws_unlock(fws);
1064 brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
1065 addr);
1066 } else {
1067 brcmf_dbg(TRACE, "use existing\n");
1068 WARN_ON(entry->mac_handle != mac_handle);
1069
1070 }
1071 }
1072 return 0;
1073}
1074
1075static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
1076 u8 type, u8 *data)
1077{
1078 struct brcmf_fws_mac_descriptor *entry;
1079 u8 mac_handle;
1080 int ret;
1081
1082 mac_handle = data[0];
1083 entry = &fws->desc.nodes[mac_handle & 0x1F];
1084 if (!entry->occupied) {
1085 fws->stats.mac_ps_update_failed++;
1086 return -ESRCH;
1087 }
1088 brcmf_fws_lock(fws);
1089
1090 entry->requested_credit = 0;
1091 entry->requested_packet = 0;
1092 if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
1093 entry->state = BRCMF_FWS_STATE_OPEN;
1094 ret = BRCMF_FWS_RET_OK_SCHEDULE;
1095 } else {
1096 entry->state = BRCMF_FWS_STATE_CLOSE;
1097 brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false);
1098 brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false);
1099 brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false);
1100 brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
1101 ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
1102 }
1103 brcmf_fws_unlock(fws);
1104 return ret;
1105}
1106
1107static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
1108 u8 type, u8 *data)
1109{
1110 struct brcmf_fws_mac_descriptor *entry;
1111 u8 ifidx;
1112 int ret;
1113
1114 ifidx = data[0];
1115
1116 if (ifidx >= BRCMF_MAX_IFS) {
1117 ret = -ERANGE;
1118 goto fail;
1119 }
1120
1121 entry = &fws->desc.iface[ifidx];
1122 if (!entry->occupied) {
1123 ret = -ESRCH;
1124 goto fail;
1125 }
1126
1127 brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
1128 entry->name);
1129 brcmf_fws_lock(fws);
1130 switch (type) {
1131 case BRCMF_FWS_TYPE_INTERFACE_OPEN:
1132 entry->state = BRCMF_FWS_STATE_OPEN;
1133 ret = BRCMF_FWS_RET_OK_SCHEDULE;
1134 break;
1135 case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
1136 entry->state = BRCMF_FWS_STATE_CLOSE;
1137 ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
1138 break;
1139 default:
1140 ret = -EINVAL;
1141 brcmf_fws_unlock(fws);
1142 goto fail;
1143 }
1144 brcmf_fws_unlock(fws);
1145 return ret;
1146
1147fail:
1148 fws->stats.if_update_failed++;
1149 return ret;
1150}
1151
1152static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
1153 u8 *data)
1154{
1155 struct brcmf_fws_mac_descriptor *entry;
1156
1157 entry = &fws->desc.nodes[data[1] & 0x1F];
1158 if (!entry->occupied) {
1159 if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
1160 fws->stats.credit_request_failed++;
1161 else
1162 fws->stats.packet_request_failed++;
1163 return -ESRCH;
1164 }
1165
1166 brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
1167 brcmf_fws_get_tlv_name(type), type, entry->name,
1168 data[0], data[2]);
1169 brcmf_fws_lock(fws);
1170 if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
1171 entry->requested_credit = data[0];
1172 else
1173 entry->requested_packet = data[0];
1174
1175 entry->ac_bitmap = data[2];
1176 brcmf_fws_unlock(fws);
1177 return BRCMF_FWS_RET_OK_SCHEDULE;
1178}
1179
1180static void
1181brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry,
1182 struct sk_buff *skb)
1183{
1184 if (entry->requested_credit > 0) {
1185 entry->requested_credit--;
1186 brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
1187 brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1);
1188 if (entry->state != BRCMF_FWS_STATE_CLOSE)
1189 brcmf_err("requested credit set while mac not closed!\n");
1190 } else if (entry->requested_packet > 0) {
1191 entry->requested_packet--;
1192 brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
1193 brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
1194 if (entry->state != BRCMF_FWS_STATE_CLOSE)
1195 brcmf_err("requested packet set while mac not closed!\n");
1196 } else {
1197 brcmf_skb_if_flags_set_field(skb, REQUESTED, 0);
1198 brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
1199 }
1200}
1201
1202static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb)
1203{
1204 struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
1205
1206 if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) &&
1207 (entry->state == BRCMF_FWS_STATE_CLOSE))
1208 entry->requested_credit++;
1209}
1210
1211static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
1212 u8 fifo, u8 credits)
1213{
1214 int lender_ac;
1215 int *borrowed;
1216 int *fifo_credit;
1217
1218 if (!credits)
1219 return;
1220
1221 fws->fifo_credit_map |= 1 << fifo;
1222
1223 if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
1224 (fws->credits_borrowed[0])) {
1225 for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
1226 lender_ac--) {
1227 borrowed = &fws->credits_borrowed[lender_ac];
1228 if (*borrowed) {
1229 fws->fifo_credit_map |= (1 << lender_ac);
1230 fifo_credit = &fws->fifo_credit[lender_ac];
1231 if (*borrowed >= credits) {
1232 *borrowed -= credits;
1233 *fifo_credit += credits;
1234 return;
1235 } else {
1236 credits -= *borrowed;
1237 *fifo_credit += *borrowed;
1238 *borrowed = 0;
1239 }
1240 }
1241 }
1242 }
1243
1244 fws->fifo_credit[fifo] += credits;
1245 if (fws->fifo_credit[fifo] > fws->init_fifo_credit[fifo])
1246 fws->fifo_credit[fifo] = fws->init_fifo_credit[fifo];
1247
1248}
1249
1250static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
1251{
1252
1253 if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
1254 (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
1255 queue_work(fws->fws_wq, &fws->fws_dequeue_work);
1256}
1257
1258static int brcmf_fws_enq(struct brcmf_fws_info *fws,
1259 enum brcmf_fws_skb_state state, int fifo,
1260 struct sk_buff *p)
1261{
1262 struct brcmf_pub *drvr = fws->drvr;
1263 int prec = 2 * fifo;
1264 u32 *qfull_stat = &fws->stats.delayq_full_error;
1265 struct brcmf_fws_mac_descriptor *entry;
1266 struct pktq *pq;
1267 struct sk_buff_head *queue;
1268 struct sk_buff *p_head;
1269 struct sk_buff *p_tail;
1270 u32 fr_new;
1271 u32 fr_compare;
1272
1273 entry = brcmf_skbcb(p)->mac;
1274 if (entry == NULL) {
1275 bphy_err(drvr, "no mac descriptor found for skb %p\n", p);
1276 return -ENOENT;
1277 }
1278
1279 brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p);
1280 if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
1281 prec += 1;
1282 qfull_stat = &fws->stats.supprq_full_error;
1283
1284
1285
1286 pq = &entry->psq;
1287 if (pktq_full(pq) || pktq_pfull(pq, prec)) {
1288 *qfull_stat += 1;
1289 return -ENFILE;
1290 }
1291 queue = &pq->q[prec].skblist;
1292
1293 p_head = skb_peek(queue);
1294 p_tail = skb_peek_tail(queue);
1295 fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN);
1296
1297 while (p_head != p_tail) {
1298 fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
1299 FREERUN);
1300
1301 if (((fr_new > fr_compare) &&
1302 ((fr_new - fr_compare) < 128)) ||
1303 ((fr_new < fr_compare) &&
1304 ((fr_compare - fr_new) > 128)))
1305 break;
1306 p_tail = skb_queue_prev(queue, p_tail);
1307 }
1308
1309 if (p_tail == NULL) {
1310
1311 __skb_queue_tail(queue, p);
1312 } else {
1313 fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
1314 FREERUN);
1315 if (((fr_new > fr_compare) &&
1316 ((fr_new - fr_compare) < 128)) ||
1317 ((fr_new < fr_compare) &&
1318 ((fr_compare - fr_new) > 128))) {
1319
1320 __skb_queue_after(queue, p_tail, p);
1321 } else {
1322
1323 __skb_insert(p, p_tail->prev, p_tail, queue);
1324 }
1325 }
1326
1327
1328 pq->len++;
1329 if (pq->hi_prec < prec)
1330 pq->hi_prec = (u8) prec;
1331 } else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) {
1332 *qfull_stat += 1;
1333 return -ENFILE;
1334 }
1335
1336
1337 fws->fifo_delay_map |= 1 << fifo;
1338 fws->fifo_enqpkt[fifo]++;
1339
1340
1341 brcmf_skbcb(p)->state = state;
1342
1343
1344
1345
1346
1347 brcmf_fws_tim_update(fws, entry, fifo, true);
1348 brcmf_fws_flow_control_check(fws, &entry->psq,
1349 brcmf_skb_if_flags_get_field(p, INDEX));
1350 return 0;
1351}
1352
1353static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
1354{
1355 struct brcmf_fws_mac_descriptor *table;
1356 struct brcmf_fws_mac_descriptor *entry;
1357 struct sk_buff *p;
1358 int num_nodes;
1359 int node_pos;
1360 int prec_out;
1361 int pmsk;
1362 int i;
1363
1364 table = (struct brcmf_fws_mac_descriptor *)&fws->desc;
1365 num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor);
1366 node_pos = fws->deq_node_pos[fifo];
1367
1368 for (i = 0; i < num_nodes; i++) {
1369 entry = &table[(node_pos + i) % num_nodes];
1370 if (!entry->occupied ||
1371 brcmf_fws_macdesc_closed(fws, entry, fifo))
1372 continue;
1373
1374 if (entry->suppressed)
1375 pmsk = 2;
1376 else
1377 pmsk = 3;
1378 p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
1379 if (p == NULL) {
1380 if (entry->suppressed) {
1381 if (entry->suppr_transit_count)
1382 continue;
1383 entry->suppressed = false;
1384 p = brcmu_pktq_mdeq(&entry->psq,
1385 1 << (fifo * 2), &prec_out);
1386 }
1387 }
1388 if (p == NULL)
1389 continue;
1390
1391 brcmf_fws_macdesc_use_req_credit(entry, p);
1392
1393
1394 fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes;
1395 brcmf_fws_flow_control_check(fws, &entry->psq,
1396 brcmf_skb_if_flags_get_field(p,
1397 INDEX)
1398 );
1399
1400
1401
1402
1403 brcmf_fws_tim_update(fws, entry, fifo, false);
1404
1405
1406
1407
1408
1409 fws->fifo_enqpkt[fifo]--;
1410 if (fws->fifo_enqpkt[fifo] == 0)
1411 fws->fifo_delay_map &= ~(1 << fifo);
1412 goto done;
1413 }
1414 p = NULL;
1415done:
1416 brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);
1417 return p;
1418}
1419
1420static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
1421 struct sk_buff *skb,
1422 u32 genbit, u16 seq)
1423{
1424 struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
1425 u32 hslot;
1426 int ret;
1427
1428 hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
1429
1430
1431 if (!entry->suppressed) {
1432 entry->suppressed = true;
1433 entry->suppr_transit_count = entry->transit_count;
1434 brcmf_dbg(DATA, "suppress %s: transit %d\n",
1435 entry->name, entry->transit_count);
1436 }
1437
1438 entry->generation = genbit;
1439
1440 brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
1441 brcmf_skbcb(skb)->htod_seq = seq;
1442 if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
1443 brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
1444 brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
1445 } else {
1446 brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
1447 }
1448 ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
1449
1450 if (ret != 0) {
1451
1452 brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true);
1453 } else {
1454
1455 brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
1456 }
1457
1458 return ret;
1459}
1460
1461static int
1462brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
1463 u32 genbit, u16 seq, u8 compcnt)
1464{
1465 struct brcmf_pub *drvr = fws->drvr;
1466 u32 fifo;
1467 u8 cnt = 0;
1468 int ret;
1469 bool remove_from_hanger = true;
1470 struct sk_buff *skb;
1471 struct brcmf_skbuff_cb *skcb;
1472 struct brcmf_fws_mac_descriptor *entry = NULL;
1473 struct brcmf_if *ifp;
1474
1475 brcmf_dbg(DATA, "flags %d\n", flags);
1476
1477 if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
1478 fws->stats.txs_discard += compcnt;
1479 else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) {
1480 fws->stats.txs_supp_core += compcnt;
1481 remove_from_hanger = false;
1482 } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) {
1483 fws->stats.txs_supp_ps += compcnt;
1484 remove_from_hanger = false;
1485 } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)
1486 fws->stats.txs_tossed += compcnt;
1487 else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)
1488 fws->stats.txs_host_tossed += compcnt;
1489 else
1490 bphy_err(drvr, "unexpected txstatus\n");
1491
1492 while (cnt < compcnt) {
1493 ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
1494 remove_from_hanger);
1495 if (ret != 0) {
1496 bphy_err(drvr, "no packet in hanger slot: hslot=%d\n",
1497 hslot);
1498 goto cont;
1499 }
1500
1501 skcb = brcmf_skbcb(skb);
1502 entry = skcb->mac;
1503 if (WARN_ON(!entry)) {
1504 brcmu_pkt_buf_free_skb(skb);
1505 goto cont;
1506 }
1507 entry->transit_count--;
1508 if (entry->suppressed && entry->suppr_transit_count)
1509 entry->suppr_transit_count--;
1510
1511 brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name,
1512 flags, skcb->htod, seq);
1513
1514
1515 fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
1516 if (fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT ||
1517 (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
1518 flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED) {
1519 brcmf_fws_return_credits(fws, fifo, 1);
1520 brcmf_fws_schedule_deq(fws);
1521 }
1522 brcmf_fws_macdesc_return_req_credit(skb);
1523
1524 ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
1525 if (ret) {
1526 brcmu_pkt_buf_free_skb(skb);
1527 goto cont;
1528 }
1529 if (!remove_from_hanger)
1530 ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
1531 genbit, seq);
1532 if (remove_from_hanger || ret)
1533 brcmf_txfinalize(ifp, skb, true);
1534
1535cont:
1536 hslot = (hslot + 1) & (BRCMF_FWS_TXSTAT_HSLOT_MASK >>
1537 BRCMF_FWS_TXSTAT_HSLOT_SHIFT);
1538 if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
1539 seq = (seq + 1) & BRCMF_SKB_HTOD_SEQ_NR_MASK;
1540
1541 cnt++;
1542 }
1543
1544 return 0;
1545}
1546
1547static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
1548 u8 *data)
1549{
1550 int i;
1551
1552 if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
1553 brcmf_dbg(INFO, "ignored\n");
1554 return BRCMF_FWS_RET_OK_NOSCHEDULE;
1555 }
1556
1557 brcmf_dbg(DATA, "enter: data %pM\n", data);
1558 brcmf_fws_lock(fws);
1559 for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
1560 brcmf_fws_return_credits(fws, i, data[i]);
1561
1562 brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
1563 fws->fifo_delay_map);
1564 brcmf_fws_unlock(fws);
1565 return BRCMF_FWS_RET_OK_SCHEDULE;
1566}
1567
1568static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 type,
1569 u8 *data)
1570{
1571 __le32 status_le;
1572 __le16 seq_le;
1573 u32 status;
1574 u32 hslot;
1575 u32 genbit;
1576 u8 flags;
1577 u16 seq;
1578 u8 compcnt;
1579 u8 compcnt_offset = BRCMF_FWS_TYPE_TXSTATUS_LEN;
1580
1581 memcpy(&status_le, data, sizeof(status_le));
1582 status = le32_to_cpu(status_le);
1583 flags = brcmf_txstatus_get_field(status, FLAGS);
1584 hslot = brcmf_txstatus_get_field(status, HSLOT);
1585 genbit = brcmf_txstatus_get_field(status, GENERATION);
1586 if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
1587 memcpy(&seq_le, &data[BRCMF_FWS_TYPE_TXSTATUS_LEN],
1588 sizeof(seq_le));
1589 seq = le16_to_cpu(seq_le);
1590 compcnt_offset += BRCMF_FWS_TYPE_SEQ_LEN;
1591 } else {
1592 seq = 0;
1593 }
1594
1595 if (type == BRCMF_FWS_TYPE_COMP_TXSTATUS)
1596 compcnt = data[compcnt_offset];
1597 else
1598 compcnt = 1;
1599 fws->stats.txs_indicate += compcnt;
1600
1601 brcmf_fws_lock(fws);
1602 brcmf_fws_txs_process(fws, flags, hslot, genbit, seq, compcnt);
1603 brcmf_fws_unlock(fws);
1604 return BRCMF_FWS_RET_OK_NOSCHEDULE;
1605}
1606
1607static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
1608{
1609 __le32 timestamp;
1610
1611 memcpy(×tamp, &data[2], sizeof(timestamp));
1612 brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],
1613 le32_to_cpu(timestamp));
1614 return 0;
1615}
1616
1617static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
1618 const struct brcmf_event_msg *e,
1619 void *data)
1620{
1621 struct brcmf_pub *drvr = ifp->drvr;
1622 struct brcmf_fws_info *fws = drvr_to_fws(drvr);
1623 int i;
1624 u8 *credits = data;
1625
1626 if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
1627 bphy_err(drvr, "event payload too small (%d)\n", e->datalen);
1628 return -EINVAL;
1629 }
1630
1631 fws->creditmap_received = true;
1632
1633 brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
1634 brcmf_fws_lock(fws);
1635 for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
1636 fws->fifo_credit[i] += credits[i] - fws->init_fifo_credit[i];
1637 fws->init_fifo_credit[i] = credits[i];
1638 if (fws->fifo_credit[i] > 0)
1639 fws->fifo_credit_map |= 1 << i;
1640 else
1641 fws->fifo_credit_map &= ~(1 << i);
1642 WARN_ONCE(fws->fifo_credit[i] < 0,
1643 "fifo_credit[%d] is negative(%d)\n", i,
1644 fws->fifo_credit[i]);
1645 }
1646 brcmf_fws_schedule_deq(fws);
1647 brcmf_fws_unlock(fws);
1648 return 0;
1649}
1650
1651static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
1652 const struct brcmf_event_msg *e,
1653 void *data)
1654{
1655 struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
1656
1657 if (fws) {
1658 brcmf_fws_lock(fws);
1659 fws->bcmc_credit_check = true;
1660 brcmf_fws_unlock(fws);
1661 }
1662 return 0;
1663}
1664
1665static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
1666 u8 start, u8 end,
1667 struct sk_buff_head *skb_list)
1668{
1669
1670 __skb_queue_head_init(skb_list);
1671
1672 if (rfi->pend_pkts == 0) {
1673 brcmf_dbg(INFO, "no packets in reorder queue\n");
1674 return;
1675 }
1676
1677 do {
1678 if (rfi->pktslots[start]) {
1679 __skb_queue_tail(skb_list, rfi->pktslots[start]);
1680 rfi->pktslots[start] = NULL;
1681 }
1682 start++;
1683 if (start > rfi->max_idx)
1684 start = 0;
1685 } while (start != end);
1686 rfi->pend_pkts -= skb_queue_len(skb_list);
1687}
1688
1689void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt)
1690{
1691 struct brcmf_pub *drvr = ifp->drvr;
1692 u8 *reorder_data;
1693 u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
1694 struct brcmf_ampdu_rx_reorder *rfi;
1695 struct sk_buff_head reorder_list;
1696 struct sk_buff *pnext;
1697 u8 flags;
1698 u32 buf_size;
1699
1700 reorder_data = ((struct brcmf_skb_reorder_data *)pkt->cb)->reorder;
1701 flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
1702 flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
1703
1704
1705 if (flags == 0xFF) {
1706 bphy_err(drvr, "invalid flags...so ignore this packet\n");
1707 brcmf_netif_rx(ifp, pkt);
1708 return;
1709 }
1710
1711 rfi = ifp->drvr->reorder_flows[flow_id];
1712 if (flags & BRCMF_RXREORDER_DEL_FLOW) {
1713 brcmf_dbg(INFO, "flow-%d: delete\n",
1714 flow_id);
1715
1716 if (rfi == NULL) {
1717 brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
1718 flow_id);
1719 brcmf_netif_rx(ifp, pkt);
1720 return;
1721 }
1722
1723 brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
1724 &reorder_list);
1725
1726 __skb_queue_tail(&reorder_list, pkt);
1727 kfree(rfi);
1728 ifp->drvr->reorder_flows[flow_id] = NULL;
1729 goto netif_rx;
1730 }
1731
1732 if (rfi == NULL) {
1733 buf_size = sizeof(*rfi);
1734 max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
1735
1736 buf_size += (max_idx + 1) * sizeof(pkt);
1737
1738
1739 brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
1740 flow_id, max_idx);
1741 rfi = kzalloc(buf_size, GFP_ATOMIC);
1742 if (rfi == NULL) {
1743 bphy_err(drvr, "failed to alloc buffer\n");
1744 brcmf_netif_rx(ifp, pkt);
1745 return;
1746 }
1747
1748 ifp->drvr->reorder_flows[flow_id] = rfi;
1749 rfi->pktslots = (struct sk_buff **)(rfi + 1);
1750 rfi->max_idx = max_idx;
1751 }
1752 if (flags & BRCMF_RXREORDER_NEW_HOLE) {
1753 if (rfi->pend_pkts) {
1754 brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
1755 rfi->exp_idx,
1756 &reorder_list);
1757 WARN_ON(rfi->pend_pkts);
1758 } else {
1759 __skb_queue_head_init(&reorder_list);
1760 }
1761 rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
1762 rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
1763 rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
1764 rfi->pktslots[rfi->cur_idx] = pkt;
1765 rfi->pend_pkts++;
1766 brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
1767 flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
1768 } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
1769 cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
1770 exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
1771
1772 if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
1773
1774
1775 if (rfi->pktslots[cur_idx] != NULL) {
1776 brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
1777 brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
1778 rfi->pktslots[cur_idx] = NULL;
1779 }
1780 rfi->pktslots[cur_idx] = pkt;
1781 rfi->pend_pkts++;
1782 rfi->cur_idx = cur_idx;
1783 brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
1784 flow_id, cur_idx, exp_idx, rfi->pend_pkts);
1785
1786
1787
1788
1789 return;
1790 }
1791 if (rfi->exp_idx == cur_idx) {
1792 if (rfi->pktslots[cur_idx] != NULL) {
1793 brcmf_dbg(INFO, "error buffer pending..free it\n");
1794 brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
1795 rfi->pktslots[cur_idx] = NULL;
1796 }
1797 rfi->pktslots[cur_idx] = pkt;
1798 rfi->pend_pkts++;
1799
1800
1801
1802
1803 brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
1804 flow_id, cur_idx, exp_idx, rfi->pend_pkts);
1805
1806 rfi->cur_idx = cur_idx;
1807 rfi->exp_idx = exp_idx;
1808
1809 brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
1810 &reorder_list);
1811 brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
1812 flow_id, skb_queue_len(&reorder_list),
1813 rfi->pend_pkts);
1814 } else {
1815 u8 end_idx;
1816
1817 brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
1818 flow_id, flags, rfi->cur_idx, rfi->exp_idx,
1819 cur_idx, exp_idx);
1820 if (flags & BRCMF_RXREORDER_FLUSH_ALL)
1821 end_idx = rfi->exp_idx;
1822 else
1823 end_idx = exp_idx;
1824
1825
1826 brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
1827 &reorder_list);
1828
1829 if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
1830 __skb_queue_tail(&reorder_list, pkt);
1831 } else {
1832 rfi->pktslots[cur_idx] = pkt;
1833 rfi->pend_pkts++;
1834 }
1835 rfi->exp_idx = exp_idx;
1836 rfi->cur_idx = cur_idx;
1837 }
1838 } else {
1839
1840 exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
1841
1842 brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
1843 flow_id, flags, rfi->exp_idx, exp_idx);
1844 if (flags & BRCMF_RXREORDER_FLUSH_ALL)
1845 end_idx = rfi->exp_idx;
1846 else
1847 end_idx = exp_idx;
1848
1849 brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
1850 &reorder_list);
1851 __skb_queue_tail(&reorder_list, pkt);
1852
1853 rfi->exp_idx = exp_idx;
1854 }
1855netif_rx:
1856 skb_queue_walk_safe(&reorder_list, pkt, pnext) {
1857 __skb_unlink(pkt, &reorder_list);
1858 brcmf_netif_rx(ifp, pkt);
1859 }
1860}
1861
1862void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
1863{
1864 struct brcmf_skb_reorder_data *rd;
1865 struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
1866 u8 *signal_data;
1867 s16 data_len;
1868 u8 type;
1869 u8 len;
1870 u8 *data;
1871 s32 status;
1872 s32 err;
1873
1874 brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
1875 ifp->ifidx, skb->len, siglen);
1876
1877 WARN_ON(siglen > skb->len);
1878
1879 if (!siglen)
1880 return;
1881
1882 if ((!fws) || (!fws->fw_signals)) {
1883 skb_pull(skb, siglen);
1884 return;
1885 }
1886
1887 fws->stats.header_pulls++;
1888 data_len = siglen;
1889 signal_data = skb->data;
1890
1891 status = BRCMF_FWS_RET_OK_NOSCHEDULE;
1892 while (data_len > 0) {
1893
1894 type = signal_data[0];
1895
1896
1897
1898
1899 if (type == BRCMF_FWS_TYPE_FILLER) {
1900 signal_data += 1;
1901 data_len -= 1;
1902 continue;
1903 }
1904 len = signal_data[1];
1905 data = signal_data + 2;
1906
1907 brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n",
1908 brcmf_fws_get_tlv_name(type), type, len,
1909 brcmf_fws_get_tlv_len(fws, type));
1910
1911
1912 if (data_len < len + 2)
1913 break;
1914
1915 if (len < brcmf_fws_get_tlv_len(fws, type))
1916 break;
1917
1918 err = BRCMF_FWS_RET_OK_NOSCHEDULE;
1919 switch (type) {
1920 case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
1921 rd = (struct brcmf_skb_reorder_data *)skb->cb;
1922 rd->reorder = data;
1923 break;
1924 case BRCMF_FWS_TYPE_MACDESC_ADD:
1925 case BRCMF_FWS_TYPE_MACDESC_DEL:
1926 brcmf_fws_macdesc_indicate(fws, type, data);
1927 break;
1928 case BRCMF_FWS_TYPE_MAC_OPEN:
1929 case BRCMF_FWS_TYPE_MAC_CLOSE:
1930 err = brcmf_fws_macdesc_state_indicate(fws, type, data);
1931 break;
1932 case BRCMF_FWS_TYPE_INTERFACE_OPEN:
1933 case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
1934 err = brcmf_fws_interface_state_indicate(fws, type,
1935 data);
1936 break;
1937 case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
1938 case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
1939 err = brcmf_fws_request_indicate(fws, type, data);
1940 break;
1941 case BRCMF_FWS_TYPE_TXSTATUS:
1942 case BRCMF_FWS_TYPE_COMP_TXSTATUS:
1943 brcmf_fws_txstatus_indicate(fws, type, data);
1944 break;
1945 case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
1946 err = brcmf_fws_fifocreditback_indicate(fws, data);
1947 break;
1948 case BRCMF_FWS_TYPE_RSSI:
1949 brcmf_fws_rssi_indicate(fws, *data);
1950 break;
1951 case BRCMF_FWS_TYPE_TRANS_ID:
1952 brcmf_fws_dbg_seqnum_check(fws, data);
1953 break;
1954 case BRCMF_FWS_TYPE_PKTTAG:
1955 case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP:
1956 default:
1957 fws->stats.tlv_invalid_type++;
1958 break;
1959 }
1960 if (err == BRCMF_FWS_RET_OK_SCHEDULE)
1961 status = BRCMF_FWS_RET_OK_SCHEDULE;
1962 signal_data += len + 2;
1963 data_len -= len + 2;
1964 }
1965
1966 if (data_len != 0)
1967 fws->stats.tlv_parse_failed++;
1968
1969 if (status == BRCMF_FWS_RET_OK_SCHEDULE)
1970 brcmf_fws_schedule_deq(fws);
1971
1972
1973
1974
1975 skb_pull(skb, siglen);
1976
1977
1978
1979 if (skb->len == 0)
1980 fws->stats.header_only_pkt++;
1981}
1982
1983static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
1984 struct sk_buff *p)
1985{
1986 struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
1987 struct brcmf_fws_mac_descriptor *entry = skcb->mac;
1988 u8 flags;
1989
1990 if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED)
1991 brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);
1992 flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
1993 if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
1994
1995
1996
1997
1998 flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
1999 }
2000 brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
2001 return brcmf_fws_hdrpush(fws, p);
2002}
2003
2004static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
2005 struct sk_buff *skb, int fifo)
2006{
2007 struct brcmf_pub *drvr = fws->drvr;
2008 struct brcmf_fws_mac_descriptor *entry;
2009 struct sk_buff *pktout;
2010 int qidx, hslot;
2011 int rc = 0;
2012
2013 entry = brcmf_skbcb(skb)->mac;
2014 if (entry->occupied) {
2015 qidx = 2 * fifo;
2016 if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
2017 qidx++;
2018
2019 pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb);
2020 if (pktout == NULL) {
2021 bphy_err(drvr, "%s queue %d full\n", entry->name, qidx);
2022 rc = -ENOSPC;
2023 }
2024 } else {
2025 bphy_err(drvr, "%s entry removed\n", entry->name);
2026 rc = -ENOENT;
2027 }
2028
2029 if (rc) {
2030 fws->stats.rollback_failed++;
2031 hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
2032 brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
2033 hslot, 0, 0, 1);
2034 } else {
2035 fws->stats.rollback_success++;
2036 brcmf_fws_return_credits(fws, fifo, 1);
2037 brcmf_fws_macdesc_return_req_credit(skb);
2038 }
2039}
2040
2041static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
2042{
2043 int lender_ac;
2044
2045 if (time_after(fws->borrow_defer_timestamp, jiffies)) {
2046 fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
2047 return -ENAVAIL;
2048 }
2049
2050 for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) {
2051 if (fws->fifo_credit[lender_ac] > 0) {
2052 fws->credits_borrowed[lender_ac]++;
2053 fws->fifo_credit[lender_ac]--;
2054 if (fws->fifo_credit[lender_ac] == 0)
2055 fws->fifo_credit_map &= ~(1 << lender_ac);
2056 fws->fifo_credit_map |= (1 << BRCMF_FWS_FIFO_AC_BE);
2057 brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac);
2058 return 0;
2059 }
2060 }
2061 fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
2062 return -ENAVAIL;
2063}
2064
2065static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
2066 struct sk_buff *skb)
2067{
2068 struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
2069 struct brcmf_fws_mac_descriptor *entry;
2070 int rc;
2071 u8 ifidx;
2072 u8 data_offset;
2073
2074 entry = skcb->mac;
2075 if (IS_ERR(entry))
2076 return PTR_ERR(entry);
2077
2078 data_offset = brcmf_fws_precommit_skb(fws, fifo, skb);
2079 entry->transit_count++;
2080 if (entry->suppressed)
2081 entry->suppr_transit_count++;
2082 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
2083 brcmf_fws_unlock(fws);
2084 rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
2085 brcmf_fws_lock(fws);
2086 brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
2087 skcb->if_flags, skcb->htod, rc);
2088 if (rc < 0) {
2089 entry->transit_count--;
2090 if (entry->suppressed)
2091 entry->suppr_transit_count--;
2092 (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
2093 goto rollback;
2094 }
2095
2096 fws->stats.pkt2bus++;
2097 fws->stats.send_pkts[fifo]++;
2098 if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
2099 fws->stats.requested_sent[fifo]++;
2100
2101 return rc;
2102
2103rollback:
2104 brcmf_fws_rollback_toq(fws, skb, fifo);
2105 return rc;
2106}
2107
2108static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,
2109 int fifo)
2110{
2111 struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
2112 int rc, hslot;
2113
2114 skcb->htod = 0;
2115 skcb->htod_seq = 0;
2116 hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
2117 brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
2118 brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);
2119 brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
2120 rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
2121 if (!rc)
2122 skcb->mac->seq[fifo]++;
2123 else
2124 fws->stats.generic_error++;
2125 return rc;
2126}
2127
2128int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
2129{
2130 struct brcmf_pub *drvr = ifp->drvr;
2131 struct brcmf_fws_info *fws = drvr_to_fws(drvr);
2132 struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
2133 struct ethhdr *eh = (struct ethhdr *)(skb->data);
2134 int fifo = BRCMF_FWS_FIFO_BCMC;
2135 bool multicast = is_multicast_ether_addr(eh->h_dest);
2136 int rc = 0;
2137
2138 brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
2139
2140
2141 skcb->if_flags = 0;
2142 skcb->state = BRCMF_FWS_SKBSTATE_NEW;
2143 brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
2144 if (!multicast)
2145 fifo = brcmf_fws_prio2fifo[skb->priority];
2146
2147 brcmf_fws_lock(fws);
2148 if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
2149 fws->borrow_defer_timestamp = jiffies +
2150 BRCMF_FWS_BORROW_DEFER_PERIOD;
2151
2152 skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);
2153 brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,
2154 eh->h_dest, multicast, fifo);
2155 if (!brcmf_fws_assign_htod(fws, skb, fifo)) {
2156 brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
2157 brcmf_fws_schedule_deq(fws);
2158 } else {
2159 bphy_err(drvr, "drop skb: no hanger slot\n");
2160 brcmf_txfinalize(ifp, skb, false);
2161 rc = -ENOMEM;
2162 }
2163 brcmf_fws_unlock(fws);
2164
2165 return rc;
2166}
2167
2168void brcmf_fws_reset_interface(struct brcmf_if *ifp)
2169{
2170 struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
2171
2172 brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
2173 if (!entry)
2174 return;
2175
2176 brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
2177}
2178
2179void brcmf_fws_add_interface(struct brcmf_if *ifp)
2180{
2181 struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
2182 struct brcmf_fws_mac_descriptor *entry;
2183
2184 if (!ifp->ndev || !brcmf_fws_queue_skbs(fws))
2185 return;
2186
2187 entry = &fws->desc.iface[ifp->ifidx];
2188 ifp->fws_desc = entry;
2189 brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
2190 brcmf_fws_macdesc_set_name(fws, entry);
2191 brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
2192 BRCMF_FWS_PSQ_LEN);
2193 brcmf_dbg(TRACE, "added %s\n", entry->name);
2194}
2195
2196void brcmf_fws_del_interface(struct brcmf_if *ifp)
2197{
2198 struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
2199 struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
2200
2201 if (!entry)
2202 return;
2203
2204 brcmf_fws_lock(fws);
2205 ifp->fws_desc = NULL;
2206 brcmf_dbg(TRACE, "deleting %s\n", entry->name);
2207 brcmf_fws_macdesc_cleanup(fws, &fws->desc.iface[ifp->ifidx],
2208 ifp->ifidx);
2209 brcmf_fws_macdesc_deinit(entry);
2210 brcmf_fws_cleanup(fws, ifp->ifidx);
2211 brcmf_fws_unlock(fws);
2212}
2213
2214static void brcmf_fws_dequeue_worker(struct work_struct *worker)
2215{
2216 struct brcmf_fws_info *fws;
2217 struct brcmf_pub *drvr;
2218 struct sk_buff *skb;
2219 int fifo;
2220 u32 hslot;
2221 u32 ifidx;
2222 int ret;
2223
2224 fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
2225 drvr = fws->drvr;
2226
2227 brcmf_fws_lock(fws);
2228 for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
2229 fifo--) {
2230 if (!brcmf_fws_fc_active(fws)) {
2231 while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
2232 hslot = brcmf_skb_htod_tag_get_field(skb,
2233 HSLOT);
2234 brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
2235 &skb, true);
2236 ifidx = brcmf_skb_if_flags_get_field(skb,
2237 INDEX);
2238
2239 brcmf_fws_unlock(fws);
2240 ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
2241 brcmf_fws_lock(fws);
2242 if (ret < 0)
2243 brcmf_txfinalize(brcmf_get_ifp(drvr,
2244 ifidx),
2245 skb, false);
2246 if (fws->bus_flow_blocked)
2247 break;
2248 }
2249 continue;
2250 }
2251 while ((fws->fifo_credit[fifo] > 0) ||
2252 ((!fws->bcmc_credit_check) &&
2253 (fifo == BRCMF_FWS_FIFO_BCMC))) {
2254 skb = brcmf_fws_deq(fws, fifo);
2255 if (!skb)
2256 break;
2257 fws->fifo_credit[fifo]--;
2258 if (brcmf_fws_commit_skb(fws, fifo, skb))
2259 break;
2260 if (fws->bus_flow_blocked)
2261 break;
2262 }
2263 if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
2264 (fws->fifo_credit[fifo] <= 0) &&
2265 (!fws->bus_flow_blocked)) {
2266 while (brcmf_fws_borrow_credit(fws) == 0) {
2267 skb = brcmf_fws_deq(fws, fifo);
2268 if (!skb) {
2269 brcmf_fws_return_credits(fws, fifo, 1);
2270 break;
2271 }
2272 if (brcmf_fws_commit_skb(fws, fifo, skb))
2273 break;
2274 if (fws->bus_flow_blocked)
2275 break;
2276 }
2277 }
2278 }
2279 brcmf_fws_unlock(fws);
2280}
2281
2282#ifdef DEBUG
2283static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
2284{
2285 struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
2286 struct brcmf_fws_stats *fwstats = &(drvr_to_fws(bus_if->drvr)->stats);
2287
2288 seq_printf(seq,
2289 "header_pulls: %u\n"
2290 "header_only_pkt: %u\n"
2291 "tlv_parse_failed: %u\n"
2292 "tlv_invalid_type: %u\n"
2293 "mac_update_fails: %u\n"
2294 "ps_update_fails: %u\n"
2295 "if_update_fails: %u\n"
2296 "pkt2bus: %u\n"
2297 "generic_error: %u\n"
2298 "rollback_success: %u\n"
2299 "rollback_failed: %u\n"
2300 "delayq_full: %u\n"
2301 "supprq_full: %u\n"
2302 "txs_indicate: %u\n"
2303 "txs_discard: %u\n"
2304 "txs_suppr_core: %u\n"
2305 "txs_suppr_ps: %u\n"
2306 "txs_tossed: %u\n"
2307 "txs_host_tossed: %u\n"
2308 "bus_flow_block: %u\n"
2309 "fws_flow_block: %u\n"
2310 "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
2311 "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
2312 fwstats->header_pulls,
2313 fwstats->header_only_pkt,
2314 fwstats->tlv_parse_failed,
2315 fwstats->tlv_invalid_type,
2316 fwstats->mac_update_failed,
2317 fwstats->mac_ps_update_failed,
2318 fwstats->if_update_failed,
2319 fwstats->pkt2bus,
2320 fwstats->generic_error,
2321 fwstats->rollback_success,
2322 fwstats->rollback_failed,
2323 fwstats->delayq_full_error,
2324 fwstats->supprq_full_error,
2325 fwstats->txs_indicate,
2326 fwstats->txs_discard,
2327 fwstats->txs_supp_core,
2328 fwstats->txs_supp_ps,
2329 fwstats->txs_tossed,
2330 fwstats->txs_host_tossed,
2331 fwstats->bus_flow_block,
2332 fwstats->fws_flow_block,
2333 fwstats->send_pkts[0], fwstats->send_pkts[1],
2334 fwstats->send_pkts[2], fwstats->send_pkts[3],
2335 fwstats->send_pkts[4],
2336 fwstats->requested_sent[0],
2337 fwstats->requested_sent[1],
2338 fwstats->requested_sent[2],
2339 fwstats->requested_sent[3],
2340 fwstats->requested_sent[4]);
2341
2342 return 0;
2343}
2344#else
2345static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
2346{
2347 return 0;
2348}
2349#endif
2350
2351struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr)
2352{
2353 struct brcmf_fws_info *fws;
2354 struct brcmf_if *ifp;
2355 u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
2356 int rc;
2357 u32 mode;
2358
2359 fws = kzalloc(sizeof(*fws), GFP_KERNEL);
2360 if (!fws) {
2361 rc = -ENOMEM;
2362 goto fail;
2363 }
2364
2365 spin_lock_init(&fws->spinlock);
2366
2367
2368 fws->drvr = drvr;
2369 fws->fcmode = drvr->settings->fcmode;
2370
2371 if ((drvr->bus_if->always_use_fws_queue == false) &&
2372 (fws->fcmode == BRCMF_FWS_FCMODE_NONE)) {
2373 fws->avoid_queueing = true;
2374 brcmf_dbg(INFO, "FWS queueing will be avoided\n");
2375 return fws;
2376 }
2377
2378 fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
2379 if (fws->fws_wq == NULL) {
2380 bphy_err(drvr, "workqueue creation failed\n");
2381 rc = -EBADF;
2382 goto fail;
2383 }
2384 INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
2385
2386
2387 if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
2388 tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
2389 BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
2390 BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
2391 BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;
2392
2393 rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
2394 brcmf_fws_notify_credit_map);
2395 if (rc < 0) {
2396 bphy_err(drvr, "register credit map handler failed\n");
2397 goto fail;
2398 }
2399 rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT,
2400 brcmf_fws_notify_bcmc_credit_support);
2401 if (rc < 0) {
2402 bphy_err(drvr, "register bcmc credit handler failed\n");
2403 brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
2404 goto fail;
2405 }
2406
2407
2408
2409
2410
2411 fws->fw_signals = true;
2412 ifp = brcmf_get_ifp(drvr, 0);
2413 if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
2414 bphy_err(drvr, "failed to set bdcv2 tlv signaling\n");
2415 fws->fcmode = BRCMF_FWS_FCMODE_NONE;
2416 fws->fw_signals = false;
2417 }
2418
2419 if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
2420 brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
2421
2422
2423 if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
2424 if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
2425 mode = 0;
2426 BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
2427 if (brcmf_fil_iovar_int_set(ifp,
2428 "wlfc_mode", mode) == 0) {
2429 BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
2430 }
2431 }
2432 }
2433
2434 brcmf_fws_hanger_init(&fws->hanger);
2435 brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
2436 brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
2437 brcmf_dbg(INFO, "added %s\n", fws->desc.other.name);
2438 brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
2439 BRCMF_FWS_PSQ_LEN);
2440
2441 brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
2442 fws->fw_signals ? "enabled" : "disabled", tlv);
2443 return fws;
2444
2445fail:
2446 brcmf_fws_detach_pre_delif(fws);
2447 brcmf_fws_detach_post_delif(fws);
2448 return ERR_PTR(rc);
2449}
2450
2451void brcmf_fws_detach_pre_delif(struct brcmf_fws_info *fws)
2452{
2453 if (!fws)
2454 return;
2455 if (fws->fws_wq) {
2456 destroy_workqueue(fws->fws_wq);
2457 fws->fws_wq = NULL;
2458 }
2459}
2460
2461void brcmf_fws_detach_post_delif(struct brcmf_fws_info *fws)
2462{
2463 if (!fws)
2464 return;
2465
2466
2467 brcmf_fws_lock(fws);
2468 brcmf_fws_cleanup(fws, -1);
2469 brcmf_fws_unlock(fws);
2470
2471
2472 kfree(fws);
2473}
2474
2475void brcmf_fws_debugfs_create(struct brcmf_pub *drvr)
2476{
2477
2478 brcmf_debugfs_add_entry(drvr, "fws_stats",
2479 brcmf_debugfs_fws_stats_read);
2480}
2481
2482bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws)
2483{
2484 return !fws->avoid_queueing;
2485}
2486
2487bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
2488{
2489 if (!fws->creditmap_received)
2490 return false;
2491
2492 return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
2493}
2494
2495void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
2496{
2497 u32 hslot;
2498
2499 if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
2500 brcmu_pkt_buf_free_skb(skb);
2501 return;
2502 }
2503 brcmf_fws_lock(fws);
2504 hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
2505 brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0,
2506 1);
2507 brcmf_fws_unlock(fws);
2508}
2509
2510void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
2511{
2512 struct brcmf_fws_info *fws = drvr_to_fws(drvr);
2513 struct brcmf_if *ifp;
2514 int i;
2515
2516 if (fws->avoid_queueing) {
2517 for (i = 0; i < BRCMF_MAX_IFS; i++) {
2518 ifp = drvr->iflist[i];
2519 if (!ifp || !ifp->ndev)
2520 continue;
2521 brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW,
2522 flow_blocked);
2523 }
2524 } else {
2525 fws->bus_flow_blocked = flow_blocked;
2526 if (!flow_blocked)
2527 brcmf_fws_schedule_deq(fws);
2528 else
2529 fws->stats.bus_flow_block++;
2530 }
2531}
2532