diff options
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 20 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 661 |
3 files changed, 627 insertions, 60 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index 757701d57e5a..bd16f0b17aab 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | |||
@@ -130,7 +130,7 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, | |||
130 | size_t count, loff_t *ppos) | 130 | size_t count, loff_t *ppos) |
131 | { | 131 | { |
132 | struct brcmf_fws_stats *fwstats = f->private_data; | 132 | struct brcmf_fws_stats *fwstats = f->private_data; |
133 | char buf[100]; | 133 | char buf[650]; |
134 | int res; | 134 | int res; |
135 | 135 | ||
136 | /* only allow read from start */ | 136 | /* only allow read from start */ |
@@ -145,12 +145,17 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, | |||
145 | "mac_update_fails: %u\n" | 145 | "mac_update_fails: %u\n" |
146 | "pkt2bus: %u\n" | 146 | "pkt2bus: %u\n" |
147 | "generic_error: %u\n" | 147 | "generic_error: %u\n" |
148 | "rollback_success: %u\n" | ||
149 | "rollback_failed: %u\n" | ||
150 | "delayq_full: %u\n" | ||
151 | "supprq_full: %u\n" | ||
148 | "txs_indicate: %u\n" | 152 | "txs_indicate: %u\n" |
149 | "txs_discard: %u\n" | 153 | "txs_discard: %u\n" |
150 | "txs_suppr_core: %u\n" | 154 | "txs_suppr_core: %u\n" |
151 | "txs_suppr_ps: %u\n" | 155 | "txs_suppr_ps: %u\n" |
152 | "txs_tossed: %u\n" | 156 | "txs_tossed: %u\n" |
153 | "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", | 157 | "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" |
158 | "fifo_credits_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", | ||
154 | fwstats->header_pulls, | 159 | fwstats->header_pulls, |
155 | fwstats->header_only_pkt, | 160 | fwstats->header_only_pkt, |
156 | fwstats->tlv_parse_failed, | 161 | fwstats->tlv_parse_failed, |
@@ -158,6 +163,10 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, | |||
158 | fwstats->mac_update_failed, | 163 | fwstats->mac_update_failed, |
159 | fwstats->pkt2bus, | 164 | fwstats->pkt2bus, |
160 | fwstats->generic_error, | 165 | fwstats->generic_error, |
166 | fwstats->rollback_success, | ||
167 | fwstats->rollback_failed, | ||
168 | fwstats->delayq_full_error, | ||
169 | fwstats->supprq_full_error, | ||
161 | fwstats->txs_indicate, | 170 | fwstats->txs_indicate, |
162 | fwstats->txs_discard, | 171 | fwstats->txs_discard, |
163 | fwstats->txs_supp_core, | 172 | fwstats->txs_supp_core, |
@@ -165,7 +174,12 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, | |||
165 | fwstats->txs_tossed, | 174 | fwstats->txs_tossed, |
166 | fwstats->send_pkts[0], fwstats->send_pkts[1], | 175 | fwstats->send_pkts[0], fwstats->send_pkts[1], |
167 | fwstats->send_pkts[2], fwstats->send_pkts[3], | 176 | fwstats->send_pkts[2], fwstats->send_pkts[3], |
168 | fwstats->send_pkts[4]); | 177 | fwstats->send_pkts[4], |
178 | fwstats->fifo_credits_sent[0], | ||
179 | fwstats->fifo_credits_sent[1], | ||
180 | fwstats->fifo_credits_sent[2], | ||
181 | fwstats->fifo_credits_sent[3], | ||
182 | fwstats->fifo_credits_sent[4]); | ||
169 | 183 | ||
170 | return simple_read_from_buffer(data, count, ppos, buf, res); | 184 | return simple_read_from_buffer(data, count, ppos, buf, res); |
171 | } | 185 | } |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 30c2e9b9fadd..a6b16a1e72fb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | |||
@@ -140,8 +140,14 @@ struct brcmf_fws_stats { | |||
140 | u32 header_pulls; | 140 | u32 header_pulls; |
141 | u32 pkt2bus; | 141 | u32 pkt2bus; |
142 | u32 send_pkts[5]; | 142 | u32 send_pkts[5]; |
143 | u32 fifo_credits_sent[5]; | ||
144 | u32 fifo_credits_back[6]; | ||
143 | u32 generic_error; | 145 | u32 generic_error; |
144 | u32 mac_update_failed; | 146 | u32 mac_update_failed; |
147 | u32 rollback_success; | ||
148 | u32 rollback_failed; | ||
149 | u32 delayq_full_error; | ||
150 | u32 supprq_full_error; | ||
145 | u32 txs_indicate; | 151 | u32 txs_indicate; |
146 | u32 txs_discard; | 152 | u32 txs_discard; |
147 | u32 txs_supp_core; | 153 | u32 txs_supp_core; |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 22a6eb2c7bf7..8ce79af47e66 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | |||
@@ -130,15 +130,13 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) | |||
130 | #define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 | 130 | #define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 |
131 | #define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040 | 131 | #define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040 |
132 | 132 | ||
133 | #define BRCMF_FWS_STATE_OPEN 1 | ||
134 | #define BRCMF_FWS_STATE_CLOSE 2 | ||
135 | |||
136 | #define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32 | 133 | #define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32 |
137 | #define BRCMF_FWS_MAX_IFNUM 16 | ||
138 | #define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff | 134 | #define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff |
139 | 135 | ||
140 | #define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0 | 136 | #define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0 |
141 | #define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1 | 137 | #define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1 |
138 | #define BRCMF_FWS_FLOWCONTROL_HIWATER 128 | ||
139 | #define BRCMF_FWS_FLOWCONTROL_LOWATER 64 | ||
142 | 140 | ||
143 | #define BRCMF_FWS_PSQ_PREC_COUNT ((NL80211_NUM_ACS + 1) * 2) | 141 | #define BRCMF_FWS_PSQ_PREC_COUNT ((NL80211_NUM_ACS + 1) * 2) |
144 | #define BRCMF_FWS_PSQ_LEN 256 | 142 | #define BRCMF_FWS_PSQ_LEN 256 |
@@ -313,6 +311,11 @@ enum brcmf_fws_fcmode { | |||
313 | BRCMF_FWS_FCMODE_EXPLICIT_CREDIT | 311 | BRCMF_FWS_FCMODE_EXPLICIT_CREDIT |
314 | }; | 312 | }; |
315 | 313 | ||
314 | enum brcmf_fws_mac_desc_state { | ||
315 | BRCMF_FWS_STATE_OPEN = 1, | ||
316 | BRCMF_FWS_STATE_CLOSE | ||
317 | }; | ||
318 | |||
316 | /** | 319 | /** |
317 | * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface | 320 | * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface |
318 | * | 321 | * |
@@ -338,10 +341,16 @@ struct brcmf_fws_mac_descriptor { | |||
338 | u8 generation; | 341 | u8 generation; |
339 | u8 ac_bitmap; | 342 | u8 ac_bitmap; |
340 | u8 requested_credit; | 343 | u8 requested_credit; |
344 | u8 requested_packet; | ||
341 | u8 ea[ETH_ALEN]; | 345 | u8 ea[ETH_ALEN]; |
342 | u8 seq[BRCMF_FWS_FIFO_COUNT]; | 346 | u8 seq[BRCMF_FWS_FIFO_COUNT]; |
343 | struct pktq psq; | 347 | struct pktq psq; |
344 | int transit_count; | 348 | int transit_count; |
349 | int suppress_count; | ||
350 | int suppr_transit_count; | ||
351 | bool send_tim_signal; | ||
352 | u8 traffic_pending_bmp; | ||
353 | u8 traffic_lastreported_bmp; | ||
345 | }; | 354 | }; |
346 | 355 | ||
347 | #define BRCMF_FWS_HANGER_MAXITEMS 1024 | 356 | #define BRCMF_FWS_HANGER_MAXITEMS 1024 |
@@ -394,14 +403,25 @@ struct brcmf_fws_hanger { | |||
394 | struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS]; | 403 | struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS]; |
395 | }; | 404 | }; |
396 | 405 | ||
406 | struct brcmf_fws_macdesc_table { | ||
407 | struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE]; | ||
408 | struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS]; | ||
409 | struct brcmf_fws_mac_descriptor other; | ||
410 | }; | ||
411 | |||
397 | struct brcmf_fws_info { | 412 | struct brcmf_fws_info { |
398 | struct brcmf_pub *drvr; | 413 | struct brcmf_pub *drvr; |
399 | struct brcmf_fws_stats stats; | 414 | struct brcmf_fws_stats stats; |
400 | struct brcmf_fws_hanger hanger; | 415 | struct brcmf_fws_hanger hanger; |
401 | struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE]; | ||
402 | struct brcmf_fws_mac_descriptor other; | ||
403 | enum brcmf_fws_fcmode fcmode; | 416 | enum brcmf_fws_fcmode fcmode; |
417 | struct brcmf_fws_macdesc_table desc; | ||
418 | struct workqueue_struct *fws_wq; | ||
419 | struct work_struct fws_dequeue_work; | ||
420 | u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT]; | ||
404 | int fifo_credit[BRCMF_FWS_FIFO_COUNT]; | 421 | int fifo_credit[BRCMF_FWS_FIFO_COUNT]; |
422 | int deq_node_pos[BRCMF_FWS_FIFO_COUNT]; | ||
423 | u32 fifo_credit_map; | ||
424 | u32 fifo_delay_map; | ||
405 | }; | 425 | }; |
406 | 426 | ||
407 | /* | 427 | /* |
@@ -465,7 +485,7 @@ static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, | |||
465 | for (prec = 0; prec < q->num_prec; prec++) { | 485 | for (prec = 0; prec < q->num_prec; prec++) { |
466 | skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); | 486 | skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); |
467 | while (skb) { | 487 | while (skb) { |
468 | brcmf_txfinalize(fws->drvr, skb, false); | 488 | brcmu_pkt_buf_free_skb(skb); |
469 | skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); | 489 | skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); |
470 | } | 490 | } |
471 | } | 491 | } |
@@ -491,7 +511,7 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) | |||
491 | while (i != h->slot_pos) { | 511 | while (i != h->slot_pos) { |
492 | if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { | 512 | if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { |
493 | h->slot_pos = i; | 513 | h->slot_pos = i; |
494 | return i; | 514 | goto done; |
495 | } | 515 | } |
496 | i++; | 516 | i++; |
497 | if (i == BRCMF_FWS_HANGER_MAXITEMS) | 517 | if (i == BRCMF_FWS_HANGER_MAXITEMS) |
@@ -499,7 +519,10 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) | |||
499 | } | 519 | } |
500 | brcmf_err("all slots occupied\n"); | 520 | brcmf_err("all slots occupied\n"); |
501 | h->failed_slotfind++; | 521 | h->failed_slotfind++; |
502 | return BRCMF_FWS_HANGER_MAXITEMS; | 522 | i = BRCMF_FWS_HANGER_MAXITEMS; |
523 | done: | ||
524 | brcmf_dbg(TRACE, "exit: %d\n", i); | ||
525 | return i; | ||
503 | } | 526 | } |
504 | 527 | ||
505 | static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, | 528 | static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, |
@@ -545,7 +568,7 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, | |||
545 | return 0; | 568 | return 0; |
546 | } | 569 | } |
547 | 570 | ||
548 | static __used int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, | 571 | static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, |
549 | u32 slot_id, u8 gen) | 572 | u32 slot_id, u8 gen) |
550 | { | 573 | { |
551 | brcmf_dbg(TRACE, "enter\n"); | 574 | brcmf_dbg(TRACE, "enter\n"); |
@@ -583,10 +606,11 @@ static int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger, | |||
583 | return 0; | 606 | return 0; |
584 | } | 607 | } |
585 | 608 | ||
586 | static void brcmf_fws_hanger_cleanup(struct brcmf_fws_hanger *h, | 609 | static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws, |
587 | bool (*fn)(struct sk_buff *, void *), | 610 | bool (*fn)(struct sk_buff *, void *), |
588 | int ifidx) | 611 | int ifidx) |
589 | { | 612 | { |
613 | struct brcmf_fws_hanger *h = &fws->hanger; | ||
590 | struct sk_buff *skb; | 614 | struct sk_buff *skb; |
591 | int i; | 615 | int i; |
592 | enum brcmf_fws_hanger_item_state s; | 616 | enum brcmf_fws_hanger_item_state s; |
@@ -611,14 +635,16 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_hanger *h, | |||
611 | static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc, | 635 | static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc, |
612 | u8 *addr, u8 ifidx) | 636 | u8 *addr, u8 ifidx) |
613 | { | 637 | { |
614 | brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u\n", addr, ifidx); | 638 | brcmf_dbg(TRACE, |
639 | "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx); | ||
615 | desc->occupied = 1; | 640 | desc->occupied = 1; |
616 | desc->state = BRCMF_FWS_STATE_OPEN; | 641 | desc->state = BRCMF_FWS_STATE_OPEN; |
617 | desc->requested_credit = 0; | 642 | desc->requested_credit = 0; |
618 | /* depending on use may need ifp->bssidx instead */ | 643 | /* depending on use may need ifp->bssidx instead */ |
619 | desc->interface_id = ifidx; | 644 | desc->interface_id = ifidx; |
620 | desc->ac_bitmap = 0xff; /* update this when handling APSD */ | 645 | desc->ac_bitmap = 0xff; /* update this when handling APSD */ |
621 | memcpy(&desc->ea[0], addr, ETH_ALEN); | 646 | if (addr) |
647 | memcpy(&desc->ea[0], addr, ETH_ALEN); | ||
622 | } | 648 | } |
623 | 649 | ||
624 | static | 650 | static |
@@ -641,8 +667,8 @@ brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea) | |||
641 | if (ea == NULL) | 667 | if (ea == NULL) |
642 | return ERR_PTR(-EINVAL); | 668 | return ERR_PTR(-EINVAL); |
643 | 669 | ||
644 | entry = &fws->nodes[0]; | 670 | entry = &fws->desc.nodes[0]; |
645 | for (i = 0; i < ARRAY_SIZE(fws->nodes); i++) { | 671 | for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) { |
646 | if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN)) | 672 | if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN)) |
647 | return entry; | 673 | return entry; |
648 | entry++; | 674 | entry++; |
@@ -654,7 +680,7 @@ brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea) | |||
654 | static struct brcmf_fws_mac_descriptor* | 680 | static struct brcmf_fws_mac_descriptor* |
655 | brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, int ifidx, u8 *da) | 681 | brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, int ifidx, u8 *da) |
656 | { | 682 | { |
657 | struct brcmf_fws_mac_descriptor *entry = &fws->other; | 683 | struct brcmf_fws_mac_descriptor *entry = &fws->desc.other; |
658 | struct brcmf_if *ifp; | 684 | struct brcmf_if *ifp; |
659 | bool multicast; | 685 | bool multicast; |
660 | 686 | ||
@@ -670,7 +696,7 @@ brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, int ifidx, u8 *da) | |||
670 | * example, TDLS destinations have their own entry. | 696 | * example, TDLS destinations have their own entry. |
671 | */ | 697 | */ |
672 | entry = NULL; | 698 | entry = NULL; |
673 | if ((/* ifp->iftype == 0 ||*/ multicast) && ifp->fws_desc) | 699 | if (multicast && ifp->fws_desc) |
674 | entry = ifp->fws_desc; | 700 | entry = ifp->fws_desc; |
675 | 701 | ||
676 | if (entry != NULL && multicast) | 702 | if (entry != NULL && multicast) |
@@ -678,13 +704,35 @@ brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, int ifidx, u8 *da) | |||
678 | 704 | ||
679 | entry = brcmf_fws_mac_descriptor_lookup(fws, da); | 705 | entry = brcmf_fws_mac_descriptor_lookup(fws, da); |
680 | if (IS_ERR(entry)) | 706 | if (IS_ERR(entry)) |
681 | entry = &fws->other; | 707 | entry = &fws->desc.other; |
682 | 708 | ||
683 | done: | 709 | done: |
684 | brcmf_dbg(TRACE, "exit: entry=%p\n", entry); | 710 | brcmf_dbg(TRACE, "exit: entry=%p\n", entry); |
685 | return entry; | 711 | return entry; |
686 | } | 712 | } |
687 | 713 | ||
714 | static bool brcmf_fws_mac_desc_ready(struct brcmf_fws_mac_descriptor *entry, | ||
715 | int fifo) | ||
716 | { | ||
717 | bool ready; | ||
718 | |||
719 | /* | ||
720 | * destination entry is ready when firmware says it is OPEN | ||
721 | * and there are no packets enqueued for it. | ||
722 | */ | ||
723 | ready = entry->state == BRCMF_FWS_STATE_OPEN && | ||
724 | !entry->suppressed && | ||
725 | brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0; | ||
726 | |||
727 | /* | ||
728 | * Or when the destination entry is CLOSED, but firmware has | ||
729 | * specifically requested packets for this entry. | ||
730 | */ | ||
731 | ready = ready || (entry->state == BRCMF_FWS_STATE_CLOSE && | ||
732 | (entry->requested_credit + entry->requested_packet)); | ||
733 | return ready; | ||
734 | } | ||
735 | |||
688 | static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws, | 736 | static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws, |
689 | struct brcmf_fws_mac_descriptor *entry, | 737 | struct brcmf_fws_mac_descriptor *entry, |
690 | int ifidx) | 738 | int ifidx) |
@@ -743,13 +791,53 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) | |||
743 | matchfn = brcmf_fws_ifidx_match; | 791 | matchfn = brcmf_fws_ifidx_match; |
744 | 792 | ||
745 | /* cleanup individual nodes */ | 793 | /* cleanup individual nodes */ |
746 | table = &fws->nodes[0]; | 794 | table = &fws->desc.nodes[0]; |
747 | for (i = 0; i < ARRAY_SIZE(fws->nodes); i++) | 795 | for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) |
748 | brcmf_fws_mac_desc_cleanup(fws, &table[i], ifidx); | 796 | brcmf_fws_mac_desc_cleanup(fws, &table[i], ifidx); |
749 | 797 | ||
750 | brcmf_fws_mac_desc_cleanup(fws, &fws->other, ifidx); | 798 | brcmf_fws_mac_desc_cleanup(fws, &fws->desc.other, ifidx); |
751 | brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx); | 799 | brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx); |
752 | brcmf_fws_hanger_cleanup(&fws->hanger, matchfn, ifidx); | 800 | brcmf_fws_hanger_cleanup(fws, matchfn, ifidx); |
801 | } | ||
802 | |||
803 | static void brcmf_fws_tim_update(struct brcmf_fws_info *ctx, | ||
804 | struct brcmf_fws_mac_descriptor *entry, | ||
805 | int prec) | ||
806 | { | ||
807 | brcmf_dbg(TRACE, "enter: ea=%pM\n", entry->ea); | ||
808 | if (entry->state == BRCMF_FWS_STATE_CLOSE) { | ||
809 | /* check delayedQ and suppressQ in one call using bitmap */ | ||
810 | if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0) | ||
811 | entry->traffic_pending_bmp = | ||
812 | entry->traffic_pending_bmp & ~NBITVAL(prec); | ||
813 | else | ||
814 | entry->traffic_pending_bmp = | ||
815 | entry->traffic_pending_bmp | NBITVAL(prec); | ||
816 | } | ||
817 | /* request a TIM update to firmware at the next piggyback opportunity */ | ||
818 | if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) | ||
819 | entry->send_tim_signal = true; | ||
820 | } | ||
821 | |||
822 | static void | ||
823 | brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, | ||
824 | u8 if_id) | ||
825 | { | ||
826 | struct brcmf_if *ifp = fws->drvr->iflist[if_id]; | ||
827 | |||
828 | brcmf_dbg(TRACE, | ||
829 | "enter: bssidx=%d, ifidx=%d\n", ifp->bssidx, ifp->ifidx); | ||
830 | if (WARN_ON(!ifp)) | ||
831 | return; | ||
832 | |||
833 | if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && | ||
834 | pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER) | ||
835 | brcmf_txflowblock_if(ifp, | ||
836 | BRCMF_NETIF_STOP_REASON_FWS_FC, false); | ||
837 | if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && | ||
838 | pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) | ||
839 | brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true); | ||
840 | return; | ||
753 | } | 841 | } |
754 | 842 | ||
755 | static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) | 843 | static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) |
@@ -770,7 +858,7 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) | |||
770 | ifidx = *data++; | 858 | ifidx = *data++; |
771 | addr = data; | 859 | addr = data; |
772 | 860 | ||
773 | entry = &fws->nodes[mac_handle & 0x1F]; | 861 | entry = &fws->desc.nodes[mac_handle & 0x1F]; |
774 | if (type == BRCMF_FWS_TYPE_MACDESC_DEL) { | 862 | if (type == BRCMF_FWS_TYPE_MACDESC_DEL) { |
775 | brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx); | 863 | brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx); |
776 | if (entry->occupied) | 864 | if (entry->occupied) |
@@ -808,25 +896,241 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) | |||
808 | return 0; | 896 | return 0; |
809 | } | 897 | } |
810 | 898 | ||
899 | static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, | ||
900 | u8 fifo, u8 credits) | ||
901 | { | ||
902 | if (!credits) | ||
903 | return; | ||
904 | |||
905 | fws->fifo_credit_map |= 1 << fifo; | ||
906 | fws->fifo_credit[fifo] += credits; | ||
907 | } | ||
908 | |||
909 | static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws) | ||
910 | { | ||
911 | /* only schedule dequeue when there are credits for delayed traffic */ | ||
912 | if (fws->fifo_credit_map & fws->fifo_delay_map) | ||
913 | queue_work(fws->fws_wq, &fws->fws_dequeue_work); | ||
914 | } | ||
915 | |||
916 | static void brcmf_skb_pick_up_credit(struct brcmf_fws_info *fws, int fifo, | ||
917 | struct sk_buff *p) | ||
918 | { | ||
919 | struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(p)->mac; | ||
920 | |||
921 | if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { | ||
922 | if (fws->fcmode != BRCMF_FWS_FCMODE_IMPLIED_CREDIT) | ||
923 | return; | ||
924 | brcmf_fws_return_credits(fws, fifo, 1); | ||
925 | } else { | ||
926 | /* | ||
927 | * if this packet did not count against FIFO credit, it | ||
928 | * must have taken a requested_credit from the destination | ||
929 | * entry (for pspoll etc.) | ||
930 | */ | ||
931 | if (!brcmf_skb_if_flags_get_field(p, REQUESTED)) | ||
932 | entry->requested_credit++; | ||
933 | } | ||
934 | brcmf_fws_schedule_deq(fws); | ||
935 | } | ||
936 | |||
937 | static int brcmf_fws_enq(struct brcmf_fws_info *fws, | ||
938 | enum brcmf_fws_skb_state state, int fifo, | ||
939 | struct sk_buff *p) | ||
940 | { | ||
941 | int prec = 2 * fifo; | ||
942 | u32 *qfull_stat = &fws->stats.delayq_full_error; | ||
943 | |||
944 | struct brcmf_fws_mac_descriptor *entry; | ||
945 | |||
946 | entry = brcmf_skbcb(p)->mac; | ||
947 | if (entry == NULL) { | ||
948 | brcmf_err("no mac descriptor found for skb %p\n", p); | ||
949 | return -ENOENT; | ||
950 | } | ||
951 | |||
952 | brcmf_dbg(TRACE, "enter: ea=%pM, qlen=%d\n", entry->ea, entry->psq.len); | ||
953 | if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { | ||
954 | prec += 1; | ||
955 | qfull_stat = &fws->stats.supprq_full_error; | ||
956 | } | ||
957 | |||
958 | if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) { | ||
959 | *qfull_stat += 1; | ||
960 | return -ENFILE; | ||
961 | } | ||
962 | |||
963 | /* increment total enqueued packet count */ | ||
964 | fws->fifo_delay_map |= 1 << fifo; | ||
965 | fws->fifo_enqpkt[fifo]++; | ||
966 | |||
967 | /* update the sk_buff state */ | ||
968 | brcmf_skbcb(p)->state = state; | ||
969 | if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) | ||
970 | entry->suppress_count++; | ||
971 | |||
972 | /* | ||
973 | * A packet has been pushed so update traffic | ||
974 | * availability bitmap, if applicable | ||
975 | */ | ||
976 | brcmf_fws_tim_update(fws, entry, fifo); | ||
977 | brcmf_fws_flow_control_check(fws, &entry->psq, | ||
978 | brcmf_skb_if_flags_get_field(p, INDEX)); | ||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) | ||
983 | { | ||
984 | struct brcmf_fws_mac_descriptor *table; | ||
985 | struct brcmf_fws_mac_descriptor *entry; | ||
986 | struct sk_buff *p; | ||
987 | int use_credit = 1; | ||
988 | int num_nodes; | ||
989 | int node_pos; | ||
990 | int prec_out; | ||
991 | int pmsk = 3; | ||
992 | int i; | ||
993 | |||
994 | table = (struct brcmf_fws_mac_descriptor *)&fws->desc; | ||
995 | num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor); | ||
996 | node_pos = fws->deq_node_pos[fifo]; | ||
997 | |||
998 | for (i = 0; i < num_nodes; i++) { | ||
999 | entry = &table[(node_pos + i) % num_nodes]; | ||
1000 | if (!entry->occupied) | ||
1001 | continue; | ||
1002 | |||
1003 | if (entry->suppressed) | ||
1004 | pmsk = 2; | ||
1005 | p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out); | ||
1006 | if (p == NULL) { | ||
1007 | if (entry->suppressed) { | ||
1008 | if (entry->suppr_transit_count > | ||
1009 | entry->suppress_count) | ||
1010 | return NULL; | ||
1011 | entry->suppressed = false; | ||
1012 | p = brcmu_pktq_mdeq(&entry->psq, | ||
1013 | 1 << (fifo * 2), &prec_out); | ||
1014 | } | ||
1015 | } | ||
1016 | if (p == NULL) | ||
1017 | continue; | ||
1018 | |||
1019 | /* did the packet come from suppress sub-queue? */ | ||
1020 | if (entry->requested_credit > 0) { | ||
1021 | entry->requested_credit--; | ||
1022 | /* | ||
1023 | * if the packet was pulled out while destination is in | ||
1024 | * closed state but had a non-zero packets requested, | ||
1025 | * then this should not count against the FIFO credit. | ||
1026 | * That is due to the fact that the firmware will | ||
1027 | * most likely hold onto this packet until a suitable | ||
1028 | * time later to push it to the appropriate AC FIFO. | ||
1029 | */ | ||
1030 | if (entry->state == BRCMF_FWS_STATE_CLOSE) | ||
1031 | use_credit = 0; | ||
1032 | } else if (entry->requested_packet > 0) { | ||
1033 | entry->requested_packet--; | ||
1034 | brcmf_skb_if_flags_set_field(p, REQUESTED, 1); | ||
1035 | if (entry->state == BRCMF_FWS_STATE_CLOSE) | ||
1036 | use_credit = 0; | ||
1037 | } | ||
1038 | brcmf_skb_if_flags_set_field(p, CREDITCHECK, use_credit); | ||
1039 | |||
1040 | /* move dequeue position to ensure fair round-robin */ | ||
1041 | fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes; | ||
1042 | brcmf_fws_flow_control_check(fws, &entry->psq, | ||
1043 | brcmf_skb_if_flags_get_field(p, | ||
1044 | INDEX) | ||
1045 | ); | ||
1046 | /* | ||
1047 | * A packet has been picked up, update traffic | ||
1048 | * availability bitmap, if applicable | ||
1049 | */ | ||
1050 | brcmf_fws_tim_update(fws, entry, fifo); | ||
1051 | |||
1052 | /* | ||
1053 | * decrement total enqueued fifo packets and | ||
1054 | * clear delay bitmap if done. | ||
1055 | */ | ||
1056 | fws->fifo_enqpkt[fifo]--; | ||
1057 | if (fws->fifo_enqpkt[fifo] == 0) | ||
1058 | fws->fifo_delay_map &= ~(1 << fifo); | ||
1059 | goto done; | ||
1060 | } | ||
1061 | p = NULL; | ||
1062 | done: | ||
1063 | brcmf_dbg(TRACE, "exit: fifo %d skb %p\n", fifo, p); | ||
1064 | return p; | ||
1065 | } | ||
1066 | |||
1067 | static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, | ||
1068 | struct sk_buff *skb, u32 genbit) | ||
1069 | { | ||
1070 | struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; | ||
1071 | u32 hslot; | ||
1072 | int ret; | ||
1073 | |||
1074 | hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); | ||
1075 | |||
1076 | /* this packet was suppressed */ | ||
1077 | if (!entry->suppressed || entry->generation != genbit) { | ||
1078 | entry->suppressed = true; | ||
1079 | entry->suppress_count = brcmu_pktq_mlen(&entry->psq, | ||
1080 | 1 << (fifo * 2 + 1)); | ||
1081 | entry->suppr_transit_count = entry->transit_count; | ||
1082 | } | ||
1083 | |||
1084 | entry->generation = genbit; | ||
1085 | |||
1086 | ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); | ||
1087 | if (ret != 0) { | ||
1088 | /* suppress q is full, drop this packet */ | ||
1089 | brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, | ||
1090 | true); | ||
1091 | } else { | ||
1092 | /* | ||
1093 | * Mark suppressed to avoid a double free during | ||
1094 | * wlfc cleanup | ||
1095 | */ | ||
1096 | brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot, | ||
1097 | genbit); | ||
1098 | entry->suppress_count++; | ||
1099 | } | ||
1100 | |||
1101 | return ret; | ||
1102 | } | ||
1103 | |||
811 | static int | 1104 | static int |
812 | brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot) | 1105 | brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, |
1106 | u32 genbit) | ||
813 | { | 1107 | { |
1108 | u32 fifo; | ||
814 | int ret; | 1109 | int ret; |
1110 | bool remove_from_hanger = true; | ||
815 | struct sk_buff *skb; | 1111 | struct sk_buff *skb; |
816 | struct brcmf_fws_mac_descriptor *entry = NULL; | 1112 | struct brcmf_fws_mac_descriptor *entry = NULL; |
817 | 1113 | ||
1114 | fws->stats.txs_indicate++; | ||
1115 | |||
818 | brcmf_dbg(TRACE, "status: flags=0x%X, hslot=%d\n", | 1116 | brcmf_dbg(TRACE, "status: flags=0x%X, hslot=%d\n", |
819 | flags, hslot); | 1117 | flags, hslot); |
820 | 1118 | ||
821 | if (flags == BRCMF_FWS_TXSTATUS_DISCARD) | 1119 | if (flags == BRCMF_FWS_TXSTATUS_DISCARD) |
822 | fws->stats.txs_discard++; | 1120 | fws->stats.txs_discard++; |
823 | else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) | 1121 | else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) { |
1122 | fws->stats.txs_supp_core++; | ||
1123 | remove_from_hanger = false; | ||
1124 | } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) { | ||
1125 | fws->stats.txs_supp_ps++; | ||
1126 | remove_from_hanger = false; | ||
1127 | } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) | ||
824 | fws->stats.txs_tossed++; | 1128 | fws->stats.txs_tossed++; |
825 | else | 1129 | else |
826 | brcmf_err("unexpected txstatus\n"); | 1130 | brcmf_err("unexpected txstatus\n"); |
827 | 1131 | ||
828 | ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, | 1132 | ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, |
829 | true); | 1133 | remove_from_hanger); |
830 | if (ret != 0) { | 1134 | if (ret != 0) { |
831 | brcmf_err("no packet in hanger slot: hslot=%d\n", hslot); | 1135 | brcmf_err("no packet in hanger slot: hslot=%d\n", hslot); |
832 | return ret; | 1136 | return ret; |
@@ -834,29 +1138,61 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot) | |||
834 | 1138 | ||
835 | entry = brcmf_skbcb(skb)->mac; | 1139 | entry = brcmf_skbcb(skb)->mac; |
836 | if (WARN_ON(!entry)) { | 1140 | if (WARN_ON(!entry)) { |
837 | ret = -EINVAL; | 1141 | brcmu_pkt_buf_free_skb(skb); |
838 | goto done; | 1142 | return -EINVAL; |
839 | } | 1143 | } |
840 | 1144 | ||
841 | entry->transit_count--; | 1145 | /* pick up the implicit credit from this packet */ |
1146 | fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); | ||
1147 | brcmf_skb_pick_up_credit(fws, fifo, skb); | ||
842 | 1148 | ||
843 | done: | 1149 | if (!remove_from_hanger) |
844 | brcmf_txfinalize(fws->drvr, skb, true); | 1150 | ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit); |
845 | return ret; | 1151 | |
1152 | if (remove_from_hanger || ret) { | ||
1153 | entry->transit_count--; | ||
1154 | if (entry->suppressed) | ||
1155 | entry->suppr_transit_count--; | ||
1156 | |||
1157 | brcmf_txfinalize(fws->drvr, skb, true); | ||
1158 | } | ||
1159 | return 0; | ||
1160 | } | ||
1161 | |||
1162 | static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, | ||
1163 | u8 *data) | ||
1164 | { | ||
1165 | int i; | ||
1166 | |||
1167 | if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) { | ||
1168 | brcmf_dbg(INFO, "ignored\n"); | ||
1169 | return 0; | ||
1170 | } | ||
1171 | |||
1172 | brcmf_dbg(TRACE, "enter: data %pM\n", data); | ||
1173 | for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++) | ||
1174 | brcmf_fws_return_credits(fws, i, data[i]); | ||
1175 | |||
1176 | brcmf_dbg(INFO, "map: credit %x delay %x\n", fws->fifo_credit_map, | ||
1177 | fws->fifo_delay_map); | ||
1178 | brcmf_fws_schedule_deq(fws); | ||
1179 | return 0; | ||
846 | } | 1180 | } |
847 | 1181 | ||
848 | static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) | 1182 | static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) |
849 | { | 1183 | { |
850 | u32 status; | 1184 | u32 status; |
851 | u32 hslot; | 1185 | u32 hslot; |
1186 | u32 genbit; | ||
852 | u8 flags; | 1187 | u8 flags; |
853 | 1188 | ||
854 | fws->stats.txs_indicate++; | 1189 | fws->stats.txs_indicate++; |
855 | status = le32_to_cpu(*(__le32 *)data); | 1190 | status = le32_to_cpu(*(__le32 *)data); |
856 | flags = brcmf_txstatus_get_field(status, FLAGS); | 1191 | flags = brcmf_txstatus_get_field(status, FLAGS); |
857 | hslot = brcmf_txstatus_get_field(status, HSLOT); | 1192 | hslot = brcmf_txstatus_get_field(status, HSLOT); |
1193 | genbit = brcmf_txstatus_get_field(status, GENERATION); | ||
858 | 1194 | ||
859 | return brcmf_fws_txstatus_process(fws, flags, hslot); | 1195 | return brcmf_fws_txstatus_process(fws, flags, hslot, genbit); |
860 | } | 1196 | } |
861 | 1197 | ||
862 | static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) | 1198 | static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) |
@@ -893,9 +1229,21 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, | |||
893 | ulong flags; | 1229 | ulong flags; |
894 | u8 *credits = data; | 1230 | u8 *credits = data; |
895 | 1231 | ||
1232 | if (e->datalen < BRCMF_FWS_FIFO_COUNT) { | ||
1233 | brcmf_err("event payload too small (%d)\n", e->datalen); | ||
1234 | return -EINVAL; | ||
1235 | } | ||
1236 | |||
1237 | brcmf_dbg(TRACE, "enter: credits %pM\n", credits); | ||
896 | brcmf_fws_lock(ifp->drvr, flags); | 1238 | brcmf_fws_lock(ifp->drvr, flags); |
897 | for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) | 1239 | for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) { |
1240 | if (*credits) | ||
1241 | fws->fifo_credit_map |= 1 << i; | ||
1242 | else | ||
1243 | fws->fifo_credit_map &= ~(1 << i); | ||
898 | fws->fifo_credit[i] = *credits++; | 1244 | fws->fifo_credit[i] = *credits++; |
1245 | } | ||
1246 | brcmf_fws_schedule_deq(fws); | ||
899 | brcmf_fws_unlock(ifp->drvr, flags); | 1247 | brcmf_fws_unlock(ifp->drvr, flags); |
900 | return 0; | 1248 | return 0; |
901 | } | 1249 | } |
@@ -958,11 +1306,8 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, | |||
958 | case BRCMF_FWS_TYPE_MAC_OPEN: | 1306 | case BRCMF_FWS_TYPE_MAC_OPEN: |
959 | case BRCMF_FWS_TYPE_MAC_CLOSE: | 1307 | case BRCMF_FWS_TYPE_MAC_CLOSE: |
960 | case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT: | 1308 | case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT: |
961 | case BRCMF_FWS_TYPE_PKTTAG: | ||
962 | case BRCMF_FWS_TYPE_INTERFACE_OPEN: | 1309 | case BRCMF_FWS_TYPE_INTERFACE_OPEN: |
963 | case BRCMF_FWS_TYPE_INTERFACE_CLOSE: | 1310 | case BRCMF_FWS_TYPE_INTERFACE_CLOSE: |
964 | case BRCMF_FWS_TYPE_FIFO_CREDITBACK: | ||
965 | case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP: | ||
966 | case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET: | 1311 | case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET: |
967 | case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS: | 1312 | case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS: |
968 | case BRCMF_FWS_TYPE_COMP_TXSTATUS: | 1313 | case BRCMF_FWS_TYPE_COMP_TXSTATUS: |
@@ -974,12 +1319,17 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, | |||
974 | case BRCMF_FWS_TYPE_TXSTATUS: | 1319 | case BRCMF_FWS_TYPE_TXSTATUS: |
975 | brcmf_fws_txstatus_indicate(fws, data); | 1320 | brcmf_fws_txstatus_indicate(fws, data); |
976 | break; | 1321 | break; |
1322 | case BRCMF_FWS_TYPE_FIFO_CREDITBACK: | ||
1323 | brcmf_fws_fifocreditback_indicate(fws, data); | ||
1324 | break; | ||
977 | case BRCMF_FWS_TYPE_RSSI: | 1325 | case BRCMF_FWS_TYPE_RSSI: |
978 | brcmf_fws_rssi_indicate(fws, *data); | 1326 | brcmf_fws_rssi_indicate(fws, *data); |
979 | break; | 1327 | break; |
980 | case BRCMF_FWS_TYPE_TRANS_ID: | 1328 | case BRCMF_FWS_TYPE_TRANS_ID: |
981 | brcmf_fws_dbg_seqnum_check(fws, data); | 1329 | brcmf_fws_dbg_seqnum_check(fws, data); |
982 | break; | 1330 | break; |
1331 | case BRCMF_FWS_TYPE_PKTTAG: | ||
1332 | case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP: | ||
983 | default: | 1333 | default: |
984 | fws->stats.tlv_invalid_type++; | 1334 | fws->stats.tlv_invalid_type++; |
985 | break; | 1335 | break; |
@@ -1010,14 +1360,17 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) | |||
1010 | { | 1360 | { |
1011 | struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; | 1361 | struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; |
1012 | u8 *wlh; | 1362 | u8 *wlh; |
1013 | u16 data_offset; | 1363 | u16 data_offset = 0; |
1014 | u8 fillers; | 1364 | u8 fillers; |
1015 | __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); | 1365 | __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); |
1016 | 1366 | ||
1017 | brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u, pkttag=0x%08X\n", | 1367 | brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u, pkttag=0x%08X\n", |
1018 | entry->ea, entry->interface_id, le32_to_cpu(pkttag)); | 1368 | entry->ea, entry->interface_id, le32_to_cpu(pkttag)); |
1369 | if (entry->send_tim_signal) | ||
1370 | data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; | ||
1371 | |||
1019 | /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ | 1372 | /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ |
1020 | data_offset = 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; | 1373 | data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; |
1021 | fillers = round_up(data_offset, 4) - data_offset; | 1374 | fillers = round_up(data_offset, 4) - data_offset; |
1022 | data_offset += fillers; | 1375 | data_offset += fillers; |
1023 | 1376 | ||
@@ -1029,6 +1382,15 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) | |||
1029 | memcpy(&wlh[2], &pkttag, sizeof(pkttag)); | 1382 | memcpy(&wlh[2], &pkttag, sizeof(pkttag)); |
1030 | wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2; | 1383 | wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2; |
1031 | 1384 | ||
1385 | if (entry->send_tim_signal) { | ||
1386 | entry->send_tim_signal = 0; | ||
1387 | wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP; | ||
1388 | wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; | ||
1389 | wlh[2] = entry->mac_handle; | ||
1390 | wlh[3] = entry->traffic_pending_bmp; | ||
1391 | wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2; | ||
1392 | entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; | ||
1393 | } | ||
1032 | if (fillers) | 1394 | if (fillers) |
1033 | memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); | 1395 | memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); |
1034 | 1396 | ||
@@ -1049,7 +1411,7 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, | |||
1049 | u8 ifidx; | 1411 | u8 ifidx; |
1050 | u8 flags; | 1412 | u8 flags; |
1051 | 1413 | ||
1052 | header_needed = skcb->state == BRCMF_FWS_SKBSTATE_NEW; | 1414 | header_needed = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED; |
1053 | 1415 | ||
1054 | if (header_needed) { | 1416 | if (header_needed) { |
1055 | /* obtaining free slot may fail, but that will be caught | 1417 | /* obtaining free slot may fail, but that will be caught |
@@ -1099,6 +1461,133 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, | |||
1099 | return rc; | 1461 | return rc; |
1100 | } | 1462 | } |
1101 | 1463 | ||
1464 | static int | ||
1465 | brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb) | ||
1466 | { | ||
1467 | /* | ||
1468 | put the packet back to the head of queue | ||
1469 | |||
1470 | - suppressed packet goes back to suppress sub-queue | ||
1471 | - pull out the header, if new or delayed packet | ||
1472 | |||
1473 | Note: hslot is used only when header removal is done. | ||
1474 | */ | ||
1475 | struct brcmf_fws_mac_descriptor *entry; | ||
1476 | enum brcmf_fws_skb_state state; | ||
1477 | struct sk_buff *pktout; | ||
1478 | int rc = 0; | ||
1479 | int fifo; | ||
1480 | int hslot; | ||
1481 | u8 ifidx; | ||
1482 | |||
1483 | fifo = brcmf_skb_if_flags_get_field(skb, FIFO); | ||
1484 | state = brcmf_skbcb(skb)->state; | ||
1485 | entry = brcmf_skbcb(skb)->mac; | ||
1486 | |||
1487 | if (entry != NULL) { | ||
1488 | if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { | ||
1489 | /* wl-header is saved for suppressed packets */ | ||
1490 | pktout = brcmu_pktq_penq_head(&entry->psq, 2 * fifo + 1, | ||
1491 | skb); | ||
1492 | if (pktout == NULL) { | ||
1493 | brcmf_err("suppress queue full\n"); | ||
1494 | rc = -ENOSPC; | ||
1495 | } | ||
1496 | } else { | ||
1497 | hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); | ||
1498 | |||
1499 | /* remove header first */ | ||
1500 | rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); | ||
1501 | if (rc) { | ||
1502 | brcmf_err("header removal failed\n"); | ||
1503 | /* free the hanger slot */ | ||
1504 | brcmf_fws_hanger_poppkt(&fws->hanger, hslot, | ||
1505 | &pktout, true); | ||
1506 | brcmf_txfinalize(fws->drvr, skb, false); | ||
1507 | rc = -EINVAL; | ||
1508 | goto fail; | ||
1509 | } | ||
1510 | |||
1511 | /* delay-q packets are going to delay-q */ | ||
1512 | pktout = brcmu_pktq_penq_head(&entry->psq, | ||
1513 | 2 * fifo, skb); | ||
1514 | if (pktout == NULL) { | ||
1515 | brcmf_err("delay queue full\n"); | ||
1516 | rc = -ENOSPC; | ||
1517 | } | ||
1518 | |||
1519 | /* free the hanger slot */ | ||
1520 | brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &pktout, | ||
1521 | true); | ||
1522 | |||
1523 | /* decrement sequence count */ | ||
1524 | entry->seq[fifo]--; | ||
1525 | } | ||
1526 | /* | ||
1527 | if this packet did not count against FIFO credit, it must have | ||
1528 | taken a requested_credit from the firmware (for pspoll etc.) | ||
1529 | */ | ||
1530 | if (!(brcmf_skbcb(skb)->if_flags & | ||
1531 | BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) | ||
1532 | entry->requested_credit++; | ||
1533 | } else { | ||
1534 | brcmf_err("no mac entry linked\n"); | ||
1535 | rc = -ENOENT; | ||
1536 | } | ||
1537 | |||
1538 | |||
1539 | fail: | ||
1540 | if (rc) | ||
1541 | fws->stats.rollback_failed++; | ||
1542 | else | ||
1543 | fws->stats.rollback_success++; | ||
1544 | return rc; | ||
1545 | } | ||
1546 | |||
1547 | static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, | ||
1548 | struct sk_buff *skb) | ||
1549 | { | ||
1550 | struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; | ||
1551 | int *credit = &fws->fifo_credit[fifo]; | ||
1552 | int use_credit = 1; | ||
1553 | |||
1554 | brcmf_dbg(TRACE, "enter: ac=%d, credits=%d\n", fifo, *credit); | ||
1555 | |||
1556 | if (entry->requested_credit > 0) { | ||
1557 | /* | ||
1558 | * if the packet was pulled out while destination is in | ||
1559 | * closed state but had a non-zero packets requested, | ||
1560 | * then this should not count against the FIFO credit. | ||
1561 | * That is due to the fact that the firmware will | ||
1562 | * most likely hold onto this packet until a suitable | ||
1563 | * time later to push it to the appropriate AC FIFO. | ||
1564 | */ | ||
1565 | entry->requested_credit--; | ||
1566 | if (entry->state == BRCMF_FWS_STATE_CLOSE) | ||
1567 | use_credit = 0; | ||
1568 | } else if (entry->requested_packet > 0) { | ||
1569 | entry->requested_packet--; | ||
1570 | brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); | ||
1571 | if (entry->state == BRCMF_FWS_STATE_CLOSE) | ||
1572 | use_credit = 0; | ||
1573 | } | ||
1574 | brcmf_skb_if_flags_set_field(skb, CREDITCHECK, use_credit); | ||
1575 | if (!use_credit) { | ||
1576 | brcmf_dbg(TRACE, "exit: no creditcheck set\n"); | ||
1577 | return 0; | ||
1578 | } | ||
1579 | |||
1580 | if (!(*credit)) { | ||
1581 | brcmf_dbg(TRACE, "exit: credits depleted\n"); | ||
1582 | return -ENAVAIL; | ||
1583 | } | ||
1584 | (*credit)--; | ||
1585 | if (!(*credit)) | ||
1586 | fws->fifo_credit_map &= ~(1 << fifo); | ||
1587 | brcmf_dbg(TRACE, "exit: ac=%d, credits=%d\n", fifo, *credit); | ||
1588 | return 0; | ||
1589 | } | ||
1590 | |||
1102 | static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, | 1591 | static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, |
1103 | struct sk_buff *skb) | 1592 | struct sk_buff *skb) |
1104 | { | 1593 | { |
@@ -1114,19 +1603,24 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, | |||
1114 | rc = brcmf_fws_precommit_skb(fws, fifo, skb); | 1603 | rc = brcmf_fws_precommit_skb(fws, fifo, skb); |
1115 | if (rc < 0) { | 1604 | if (rc < 0) { |
1116 | fws->stats.generic_error++; | 1605 | fws->stats.generic_error++; |
1117 | goto done; | 1606 | goto rollback; |
1118 | } | 1607 | } |
1119 | 1608 | ||
1120 | rc = brcmf_bus_txdata(bus, skb); | 1609 | rc = brcmf_bus_txdata(bus, skb); |
1121 | if (rc < 0) | 1610 | if (rc < 0) |
1122 | goto done; | 1611 | goto rollback; |
1123 | 1612 | ||
1124 | entry->seq[fifo]++; | 1613 | entry->seq[fifo]++; |
1125 | fws->stats.pkt2bus++; | 1614 | fws->stats.pkt2bus++; |
1615 | if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { | ||
1616 | fws->stats.send_pkts[fifo]++; | ||
1617 | fws->stats.fifo_credits_sent[fifo]++; | ||
1618 | } | ||
1619 | |||
1126 | return rc; | 1620 | return rc; |
1127 | 1621 | ||
1128 | done: | 1622 | rollback: |
1129 | brcmf_txfinalize(fws->drvr, skb, false); | 1623 | rc = brcmf_fws_rollback_toq(fws, skb); |
1130 | return rc; | 1624 | return rc; |
1131 | } | 1625 | } |
1132 | 1626 | ||
@@ -1157,19 +1651,27 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) | |||
1157 | } | 1651 | } |
1158 | 1652 | ||
1159 | /* set control buffer information */ | 1653 | /* set control buffer information */ |
1654 | skcb->if_flags = 0; | ||
1160 | skcb->mac = brcmf_fws_find_mac_desc(drvr->fws, ifidx, eh->h_dest); | 1655 | skcb->mac = brcmf_fws_find_mac_desc(drvr->fws, ifidx, eh->h_dest); |
1161 | skcb->state = BRCMF_FWS_SKBSTATE_NEW; | 1656 | skcb->state = BRCMF_FWS_SKBSTATE_NEW; |
1162 | brcmf_skb_if_flags_set_field(skb, INDEX, ifidx); | 1657 | brcmf_skb_if_flags_set_field(skb, INDEX, ifidx); |
1163 | if (!multicast) | 1658 | if (!multicast) |
1164 | fifo = brcmf_fws_prio2fifo[skb->priority]; | 1659 | fifo = brcmf_fws_prio2fifo[skb->priority]; |
1165 | brcmf_skb_if_flags_set_field(skb, FIFO, fifo); | 1660 | brcmf_skb_if_flags_set_field(skb, FIFO, fifo); |
1166 | brcmf_skb_if_flags_set_field(skb, CREDITCHECK, 0); | ||
1167 | 1661 | ||
1168 | brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest, | 1662 | brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest, |
1169 | multicast, fifo); | 1663 | multicast, fifo); |
1170 | 1664 | ||
1171 | brcmf_fws_lock(drvr, flags); | 1665 | brcmf_fws_lock(drvr, flags); |
1172 | brcmf_fws_commit_skb(drvr->fws, fifo, skb); | 1666 | if (!brcmf_fws_mac_desc_ready(skcb->mac, fifo) || |
1667 | (!multicast && | ||
1668 | brcmf_fws_consume_credit(drvr->fws, fifo, skb) < 0)) { | ||
1669 | /* enqueue the packet in delayQ */ | ||
1670 | drvr->fws->fifo_delay_map |= 1 << fifo; | ||
1671 | brcmf_fws_enq(drvr->fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb); | ||
1672 | } else { | ||
1673 | brcmf_fws_commit_skb(drvr->fws, fifo, skb); | ||
1674 | } | ||
1173 | brcmf_fws_unlock(drvr, flags); | 1675 | brcmf_fws_unlock(drvr, flags); |
1174 | return 0; | 1676 | return 0; |
1175 | } | 1677 | } |
@@ -1187,6 +1689,7 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp) | |||
1187 | 1689 | ||
1188 | void brcmf_fws_add_interface(struct brcmf_if *ifp) | 1690 | void brcmf_fws_add_interface(struct brcmf_if *ifp) |
1189 | { | 1691 | { |
1692 | struct brcmf_fws_info *fws = ifp->drvr->fws; | ||
1190 | struct brcmf_fws_mac_descriptor *entry; | 1693 | struct brcmf_fws_mac_descriptor *entry; |
1191 | 1694 | ||
1192 | brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n", | 1695 | brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n", |
@@ -1194,15 +1697,11 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp) | |||
1194 | if (!ifp->drvr->fw_signals) | 1697 | if (!ifp->drvr->fw_signals) |
1195 | return; | 1698 | return; |
1196 | 1699 | ||
1197 | entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | 1700 | entry = &fws->desc.iface[ifp->ifidx]; |
1198 | if (entry) { | 1701 | ifp->fws_desc = entry; |
1199 | ifp->fws_desc = entry; | 1702 | brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx); |
1200 | brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx); | 1703 | brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, |
1201 | brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, | 1704 | BRCMF_FWS_PSQ_LEN); |
1202 | BRCMF_FWS_PSQ_LEN); | ||
1203 | } else { | ||
1204 | brcmf_err("no firmware signalling\n"); | ||
1205 | } | ||
1206 | } | 1705 | } |
1207 | 1706 | ||
1208 | void brcmf_fws_del_interface(struct brcmf_if *ifp) | 1707 | void brcmf_fws_del_interface(struct brcmf_if *ifp) |
@@ -1216,7 +1715,35 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp) | |||
1216 | ifp->fws_desc = NULL; | 1715 | ifp->fws_desc = NULL; |
1217 | brcmf_fws_clear_mac_descriptor(entry); | 1716 | brcmf_fws_clear_mac_descriptor(entry); |
1218 | brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx); | 1717 | brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx); |
1219 | kfree(entry); | 1718 | } |
1719 | |||
1720 | static void brcmf_fws_dequeue_worker(struct work_struct *worker) | ||
1721 | { | ||
1722 | struct brcmf_fws_info *fws; | ||
1723 | struct sk_buff *skb; | ||
1724 | ulong flags; | ||
1725 | int fifo; | ||
1726 | int credit; | ||
1727 | |||
1728 | fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); | ||
1729 | |||
1730 | brcmf_dbg(TRACE, "enter: fws=%p\n", fws); | ||
1731 | brcmf_fws_lock(fws->drvr, flags); | ||
1732 | for (fifo = NL80211_NUM_ACS; fifo >= 0; fifo--) { | ||
1733 | brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo, | ||
1734 | fws->fifo_credit[fifo]); | ||
1735 | for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) { | ||
1736 | skb = brcmf_fws_deq(fws, fifo); | ||
1737 | if (!skb) | ||
1738 | break; | ||
1739 | if (!brcmf_fws_commit_skb(fws, fifo, skb) && | ||
1740 | brcmf_skbcb(skb)->if_flags & | ||
1741 | BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) | ||
1742 | credit++; | ||
1743 | } | ||
1744 | fws->fifo_credit[fifo] -= credit; | ||
1745 | } | ||
1746 | brcmf_fws_unlock(fws->drvr, flags); | ||
1220 | } | 1747 | } |
1221 | 1748 | ||
1222 | int brcmf_fws_init(struct brcmf_pub *drvr) | 1749 | int brcmf_fws_init(struct brcmf_pub *drvr) |
@@ -1239,6 +1766,14 @@ int brcmf_fws_init(struct brcmf_pub *drvr) | |||
1239 | drvr->fws->drvr = drvr; | 1766 | drvr->fws->drvr = drvr; |
1240 | drvr->fws->fcmode = fcmode; | 1767 | drvr->fws->fcmode = fcmode; |
1241 | 1768 | ||
1769 | drvr->fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); | ||
1770 | if (drvr->fws->fws_wq == NULL) { | ||
1771 | brcmf_err("workqueue creation failed\n"); | ||
1772 | rc = -EBADF; | ||
1773 | goto fail; | ||
1774 | } | ||
1775 | INIT_WORK(&drvr->fws->fws_dequeue_work, brcmf_fws_dequeue_worker); | ||
1776 | |||
1242 | /* enable firmware signalling if fcmode active */ | 1777 | /* enable firmware signalling if fcmode active */ |
1243 | if (drvr->fws->fcmode != BRCMF_FWS_FCMODE_NONE) | 1778 | if (drvr->fws->fcmode != BRCMF_FWS_FCMODE_NONE) |
1244 | tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS | | 1779 | tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS | |
@@ -1257,6 +1792,9 @@ int brcmf_fws_init(struct brcmf_pub *drvr) | |||
1257 | } | 1792 | } |
1258 | 1793 | ||
1259 | brcmf_fws_hanger_init(&drvr->fws->hanger); | 1794 | brcmf_fws_hanger_init(&drvr->fws->hanger); |
1795 | brcmf_fws_init_mac_descriptor(&drvr->fws->desc.other, NULL, 0); | ||
1796 | brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT, | ||
1797 | BRCMF_FWS_PSQ_LEN); | ||
1260 | 1798 | ||
1261 | /* create debugfs file for statistics */ | 1799 | /* create debugfs file for statistics */ |
1262 | brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); | 1800 | brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); |
@@ -1306,6 +1844,15 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) | |||
1306 | 1844 | ||
1307 | brcmf_fws_lock(fws->drvr, flags); | 1845 | brcmf_fws_lock(fws->drvr, flags); |
1308 | brcmf_fws_txstatus_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED, | 1846 | brcmf_fws_txstatus_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED, |
1309 | brcmf_skb_htod_tag_get_field(skb, HSLOT)); | 1847 | brcmf_skb_htod_tag_get_field(skb, HSLOT), 0); |
1848 | /* the packet never reached firmware so reclaim credit */ | ||
1849 | if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT && | ||
1850 | brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { | ||
1851 | brcmf_fws_return_credits(fws, | ||
1852 | brcmf_skb_htod_tag_get_field(skb, | ||
1853 | FIFO), | ||
1854 | 1); | ||
1855 | brcmf_fws_schedule_deq(fws); | ||
1856 | } | ||
1310 | brcmf_fws_unlock(fws->drvr, flags); | 1857 | brcmf_fws_unlock(fws->drvr, flags); |
1311 | } | 1858 | } |