diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 216 |
1 files changed, 172 insertions, 44 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index d0cd0bf95c5a..c3e7d76dbf35 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -27,7 +27,6 @@ #include <brcmu_utils.h> #include <brcmu_wifi.h> #include "dhd.h" -#include "dhd_proto.h" #include "dhd_dbg.h" #include "dhd_bus.h" #include "fwil.h" @@ -36,6 +35,7 @@ #include "fwsignal.h" #include "p2p.h" #include "wl_cfg80211.h" +#include "proto.h" /** * DOC: Firmware Signalling @@ -105,6 +105,7 @@ static struct { }; #undef BRCMF_FWS_TLV_DEF + static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) { int i; @@ -123,6 +124,12 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) #endif /* DEBUG */ /* + * The PKTTAG tlv has additional bytes when firmware-signalling + * mode has REUSESEQ flag set. + */ +#define BRCMF_FWS_TYPE_SEQ_LEN 2 + +/* * flags used to enable tlv signalling from firmware. */ #define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001 @@ -147,8 +154,15 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) #define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01 #define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02 -#define BRCMF_FWS_RET_OK_NOSCHEDULE 0 -#define BRCMF_FWS_RET_OK_SCHEDULE 1 +#define BRCMF_FWS_RET_OK_NOSCHEDULE 0 +#define BRCMF_FWS_RET_OK_SCHEDULE 1 + +#define BRCMF_FWS_MODE_REUSESEQ_SHIFT 3 /* seq reuse */ +#define BRCMF_FWS_MODE_SET_REUSESEQ(x, val) ((x) = \ + ((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \ + (((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) +#define BRCMF_FWS_MODE_GET_REUSESEQ(x) \ + (((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1) /** * enum brcmf_fws_skb_state - indicates processing state of skb. @@ -171,6 +185,7 @@ enum brcmf_fws_skb_state { * @bus_flags: 2 bytes reserved for bus specific parameters * @if_flags: holds interface index and packet related flags. * @htod: host to device packet identifier (used in PKTTAG tlv). + * @htod_seq: this 16-bit is original seq number for every suppress packet. * @state: transmit state of the packet. * @mac: descriptor related to destination for this packet. * @@ -181,6 +196,7 @@ struct brcmf_skbuff_cb { u16 bus_flags; u16 if_flags; u32 htod; + u16 htod_seq; enum brcmf_fws_skb_state state; struct brcmf_fws_mac_descriptor *mac; }; @@ -257,6 +273,22 @@ struct brcmf_skbuff_cb { BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \ BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT) +#define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK 0x2000 +#define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT 13 +#define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK 0x1000 +#define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT 12 +#define BRCMF_SKB_HTOD_SEQ_NR_MASK 0x0fff +#define BRCMF_SKB_HTOD_SEQ_NR_SHIFT 0 + +#define brcmf_skb_htod_seq_set_field(skb, field, value) \ + brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value)) +#define brcmf_skb_htod_seq_get_field(skb, field) \ + brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT) + #define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000 #define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31 #define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000 @@ -265,8 +297,8 @@ struct brcmf_skbuff_cb { #define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24 #define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00 #define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8 -#define BRCMF_FWS_TXSTAT_PKTID_MASK 0x00FFFFFF -#define BRCMF_FWS_TXSTAT_PKTID_SHIFT 0 +#define BRCMF_FWS_TXSTAT_FREERUN_MASK 0x000000FF +#define BRCMF_FWS_TXSTAT_FREERUN_SHIFT 0 #define brcmf_txstatus_get_field(txs, field) \ brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \ @@ -443,6 +475,7 @@ struct brcmf_fws_info { unsigned long borrow_defer_timestamp; bool bus_flow_blocked; bool creditmap_received; + u8 mode; }; /* @@ -805,20 +838,23 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) brcmf_fws_hanger_cleanup(fws, matchfn, ifidx); } -static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) +static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; u8 *wlh; u16 data_offset = 0; u8 fillers; __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); + __le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq); - brcmf_dbg(TRACE, "enter: %s, idx=%d pkttag=0x%08X, hslot=%d\n", + brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n", entry->name, brcmf_skb_if_flags_get_field(skb, INDEX), - le32_to_cpu(pkttag), (le32_to_cpu(pkttag) >> 8) & 0xffff); + (le32_to_cpu(pkttag) >> 8) & 0xffff, + brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq); if (entry->send_tim_signal) data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; - + if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) + data_offset += BRCMF_FWS_TYPE_SEQ_LEN; /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; fillers = round_up(data_offset, 4) - data_offset; @@ -830,7 +866,12 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) wlh[0] = BRCMF_FWS_TYPE_PKTTAG; wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN; memcpy(&wlh[2], &pkttag, sizeof(pkttag)); - wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2; + if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) { + wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN; + memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq, + sizeof(pktseq)); + } + wlh += wlh[1] + 2; if (entry->send_tim_signal) { entry->send_tim_signal = 0; @@ -846,9 +887,7 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) if (fillers) memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); - brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX), - data_offset >> 2, skb); - return 0; + return (u8)(data_offset >> 2); } static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, @@ -856,10 +895,11 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, int fifo, bool send_immediately) { struct sk_buff *skb; - struct brcmf_bus *bus; struct brcmf_skbuff_cb *skcb; s32 err; u32 len; + u8 data_offset; + int ifidx; /* check delayedQ and suppressQ in one call using bitmap */ if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0) @@ -875,6 +915,7 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, /* create a dummy packet and sent that. The traffic */ /* bitmap info will automatically be attached to that packet */ len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 + + BRCMF_FWS_TYPE_SEQ_LEN + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 + 4 + fws->drvr->hdrlen; skb = brcmu_pkt_buf_get_skb(len); @@ -884,13 +925,13 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, skcb = brcmf_skbcb(skb); skcb->mac = entry; skcb->state = BRCMF_FWS_SKBSTATE_TIM; - bus = fws->drvr->bus_if; - err = brcmf_fws_hdrpush(fws, skb); - if (err == 0) { - brcmf_fws_unlock(fws); - err = brcmf_bus_txdata(bus, skb); - brcmf_fws_lock(fws); - } + skcb->htod = 0; + skcb->htod_seq = 0; + data_offset = brcmf_fws_hdrpush(fws, skb); + ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); + brcmf_fws_unlock(fws); + err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb); + brcmf_fws_lock(fws); if (err) brcmu_pkt_buf_free_skb(skb); return true; @@ -1172,8 +1213,13 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws, { int prec = 2 * fifo; u32 *qfull_stat = &fws->stats.delayq_full_error; - struct brcmf_fws_mac_descriptor *entry; + struct pktq *pq; + struct sk_buff_head *queue; + struct sk_buff *p_head; + struct sk_buff *p_tail; + u32 fr_new; + u32 fr_compare; entry = brcmf_skbcb(p)->mac; if (entry == NULL) { @@ -1185,9 +1231,55 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws, if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { prec += 1; qfull_stat = &fws->stats.supprq_full_error; - } - if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) { + /* Fix out of order delivery of frames. Dont assume frame */ + /* can be inserted at the end, but look for correct position */ + pq = &entry->psq; + if (pktq_full(pq) || pktq_pfull(pq, prec)) { + *qfull_stat += 1; + return -ENFILE; + } + queue = &pq->q[prec].skblist; + + p_head = skb_peek(queue); + p_tail = skb_peek_tail(queue); + fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN); + + while (p_head != p_tail) { + fr_compare = brcmf_skb_htod_tag_get_field(p_tail, + FREERUN); + /* be sure to handle wrap of 256 */ + if (((fr_new > fr_compare) && + ((fr_new - fr_compare) < 128)) || + ((fr_new < fr_compare) && + ((fr_compare - fr_new) > 128))) + break; + p_tail = skb_queue_prev(queue, p_tail); + } + /* Position found. Determine what to do */ + if (p_tail == NULL) { + /* empty list */ + __skb_queue_tail(queue, p); + } else { + fr_compare = brcmf_skb_htod_tag_get_field(p_tail, + FREERUN); + if (((fr_new > fr_compare) && + ((fr_new - fr_compare) < 128)) || + ((fr_new < fr_compare) && + ((fr_compare - fr_new) > 128))) { + /* After tail */ + __skb_queue_after(queue, p_tail, p); + } else { + /* Before tail */ + __skb_insert(p, p_tail->prev, p_tail, queue); + } + } + + /* Complete the counters and statistics */ + pq->len++; + if (pq->hi_prec < prec) + pq->hi_prec = (u8) prec; + } else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) { *qfull_stat += 1; return -ENFILE; } @@ -1277,7 +1369,8 @@ done: } static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *skb, u32 genbit) + struct sk_buff *skb, u32 genbit, + u16 seq) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; u32 hslot; @@ -1297,9 +1390,19 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, entry->generation = genbit; ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); - if (ret == 0) + if (ret == 0) { + brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit); + brcmf_skbcb(skb)->htod_seq = seq; + if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) { + brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1); + brcmf_skb_htod_seq_set_field(skb, FROMFW, 0); + } else { + brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0); + } ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); + } + if (ret != 0) { /* suppress q is full or hdrpull failed, drop this packet */ brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, @@ -1317,7 +1420,7 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, static int brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, - u32 genbit) + u32 genbit, u16 seq) { u32 fifo; int ret; @@ -1360,8 +1463,8 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, if (entry->suppressed && entry->suppr_transit_count) entry->suppr_transit_count--; - brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags, - skcb->htod); + brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name, flags, + skcb->htod, seq); /* pick up the implicit credit from this packet */ fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); @@ -1374,7 +1477,8 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, brcmf_fws_macdesc_return_req_credit(skb); if (!remove_from_hanger) - ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit); + ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit, + seq); if (remove_from_hanger || ret) brcmf_txfinalize(fws->drvr, skb, true); @@ -1406,10 +1510,12 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) { __le32 status_le; + __le16 seq_le; u32 status; u32 hslot; u32 genbit; u8 flags; + u16 seq; fws->stats.txs_indicate++; memcpy(&status_le, data, sizeof(status_le)); @@ -1417,9 +1523,16 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) flags = brcmf_txstatus_get_field(status, FLAGS); hslot = brcmf_txstatus_get_field(status, HSLOT); genbit = brcmf_txstatus_get_field(status, GENERATION); + if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) { + memcpy(&seq_le, &data[BRCMF_FWS_TYPE_PKTTAG_LEN], + sizeof(seq_le)); + seq = le16_to_cpu(seq_le); + } else { + seq = 0; + } brcmf_fws_lock(fws); - brcmf_fws_txs_process(fws, flags, hslot, genbit); + brcmf_fws_txs_process(fws, flags, hslot, genbit, seq); brcmf_fws_unlock(fws); return BRCMF_FWS_RET_OK_NOSCHEDULE; } @@ -1603,15 +1716,15 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, return 0; } -static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, +static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, struct sk_buff *p) { struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); struct brcmf_fws_mac_descriptor *entry = skcb->mac; u8 flags; - brcmf_skb_if_flags_set_field(p, TRANSMIT, 1); - brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation); + if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED) + brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation); flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST; if (brcmf_skb_if_flags_get_field(p, REQUESTED)) { /* @@ -1621,7 +1734,7 @@ static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED; } brcmf_skb_htod_tag_set_field(p, FLAGS, flags); - brcmf_fws_hdrpush(fws, p); + return brcmf_fws_hdrpush(fws, p); } static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, @@ -1652,7 +1765,7 @@ static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, fws->stats.rollback_failed++; hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, - hslot, 0); + hslot, 0, 0); } else { fws->stats.rollback_success++; brcmf_fws_return_credits(fws, fifo, 1); @@ -1689,20 +1802,21 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, { struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); struct brcmf_fws_mac_descriptor *entry; - struct brcmf_bus *bus = fws->drvr->bus_if; int rc; u8 ifidx; + u8 data_offset; entry = skcb->mac; if (IS_ERR(entry)) return PTR_ERR(entry); - brcmf_fws_precommit_skb(fws, fifo, skb); + data_offset = brcmf_fws_precommit_skb(fws, fifo, skb); entry->transit_count++; if (entry->suppressed) entry->suppr_transit_count++; + ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); brcmf_fws_unlock(fws); - rc = brcmf_bus_txdata(bus, skb); + rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb); brcmf_fws_lock(fws); brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name, skcb->if_flags, skcb->htod, rc); @@ -1732,6 +1846,8 @@ static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p, struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); int rc, hslot; + skcb->htod = 0; + skcb->htod_seq = 0; hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger); brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]); @@ -1757,7 +1873,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); /* determine the priority */ if (!skb->priority) - skb->priority = cfg80211_classify8021d(skb); + skb->priority = cfg80211_classify8021d(skb, NULL); drvr->tx_multicast += !!multicast; if (pae) @@ -1861,10 +1977,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) &skb, true); ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); - brcmf_proto_hdrpush(drvr, ifidx, 0, skb); - /* Use bus module to send data frame */ + /* Use proto layer to send data frame */ brcmf_fws_unlock(fws); - ret = brcmf_bus_txdata(drvr->bus_if, skb); + ret = brcmf_proto_txdata(drvr, ifidx, 0, skb); brcmf_fws_lock(fws); if (ret < 0) brcmf_txfinalize(drvr, skb, false); @@ -1908,6 +2023,7 @@ int brcmf_fws_init(struct brcmf_pub *drvr) struct brcmf_fws_info *fws; u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS; int rc; + u32 mode; drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); if (!drvr->fws) { @@ -1966,6 +2082,18 @@ int brcmf_fws_init(struct brcmf_pub *drvr) if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1)) brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n"); + /* Enable seq number reuse, if supported */ + if (brcmf_fil_iovar_int_get(drvr->iflist[0], "wlfc_mode", &mode) == 0) { + if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) { + mode = 0; + BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1); + if (brcmf_fil_iovar_int_set(drvr->iflist[0], + "wlfc_mode", mode) == 0) { + BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1); + } + } + } + brcmf_fws_hanger_init(&fws->hanger); brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0); brcmf_fws_macdesc_set_name(fws, &fws->desc.other); @@ -2022,7 +2150,7 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) } brcmf_fws_lock(fws); hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0); + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0); brcmf_fws_unlock(fws); } |