|
@@ -105,6 +105,9 @@ static struct {
|
|
};
|
|
};
|
|
#undef BRCMF_FWS_TLV_DEF
|
|
#undef BRCMF_FWS_TLV_DEF
|
|
|
|
|
|
|
|
+#define BRCMF_FWS_TYPE_SEQ_LEN 2
|
|
|
|
+
|
|
|
|
+
|
|
static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
|
|
static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
|
|
{
|
|
{
|
|
int i;
|
|
int i;
|
|
@@ -147,8 +150,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_PKTFROMHOST 0x01
|
|
#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02
|
|
#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.
|
|
* enum brcmf_fws_skb_state - indicates processing state of skb.
|
|
@@ -171,6 +181,7 @@ enum brcmf_fws_skb_state {
|
|
* @bus_flags: 2 bytes reserved for bus specific parameters
|
|
* @bus_flags: 2 bytes reserved for bus specific parameters
|
|
* @if_flags: holds interface index and packet related flags.
|
|
* @if_flags: holds interface index and packet related flags.
|
|
* @htod: host to device packet identifier (used in PKTTAG tlv).
|
|
* @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.
|
|
* @state: transmit state of the packet.
|
|
* @mac: descriptor related to destination for this packet.
|
|
* @mac: descriptor related to destination for this packet.
|
|
*
|
|
*
|
|
@@ -181,6 +192,7 @@ struct brcmf_skbuff_cb {
|
|
u16 bus_flags;
|
|
u16 bus_flags;
|
|
u16 if_flags;
|
|
u16 if_flags;
|
|
u32 htod;
|
|
u32 htod;
|
|
|
|
+ u16 htod_seq;
|
|
enum brcmf_fws_skb_state state;
|
|
enum brcmf_fws_skb_state state;
|
|
struct brcmf_fws_mac_descriptor *mac;
|
|
struct brcmf_fws_mac_descriptor *mac;
|
|
};
|
|
};
|
|
@@ -257,6 +269,22 @@ struct brcmf_skbuff_cb {
|
|
BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
|
|
BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
|
|
BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT)
|
|
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_MASK 0x80000000
|
|
#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31
|
|
#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31
|
|
#define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000
|
|
#define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000
|
|
@@ -265,8 +293,8 @@ struct brcmf_skbuff_cb {
|
|
#define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24
|
|
#define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24
|
|
#define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00
|
|
#define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00
|
|
#define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8
|
|
#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) \
|
|
#define brcmf_txstatus_get_field(txs, field) \
|
|
brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
|
|
brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
|
|
@@ -443,6 +471,7 @@ struct brcmf_fws_info {
|
|
unsigned long borrow_defer_timestamp;
|
|
unsigned long borrow_defer_timestamp;
|
|
bool bus_flow_blocked;
|
|
bool bus_flow_blocked;
|
|
bool creditmap_received;
|
|
bool creditmap_received;
|
|
|
|
+ u8 mode;
|
|
};
|
|
};
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -812,13 +841,16 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
|
|
u16 data_offset = 0;
|
|
u16 data_offset = 0;
|
|
u8 fillers;
|
|
u8 fillers;
|
|
__le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
|
|
__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),
|
|
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)
|
|
if (entry->send_tim_signal)
|
|
data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
|
|
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 */
|
|
/* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
|
|
data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
|
|
data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
|
|
fillers = round_up(data_offset, 4) - data_offset;
|
|
fillers = round_up(data_offset, 4) - data_offset;
|
|
@@ -830,7 +862,12 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
|
|
wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
|
|
wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
|
|
wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
|
|
wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
|
|
memcpy(&wlh[2], &pkttag, sizeof(pkttag));
|
|
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) {
|
|
if (entry->send_tim_signal) {
|
|
entry->send_tim_signal = 0;
|
|
entry->send_tim_signal = 0;
|
|
@@ -875,6 +912,7 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
|
|
/* create a dummy packet and sent that. The traffic */
|
|
/* create a dummy packet and sent that. The traffic */
|
|
/* bitmap info will automatically be attached to that packet */
|
|
/* bitmap info will automatically be attached to that packet */
|
|
len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
|
|
len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
|
|
|
|
+ BRCMF_FWS_TYPE_SEQ_LEN +
|
|
BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
|
|
BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
|
|
4 + fws->drvr->hdrlen;
|
|
4 + fws->drvr->hdrlen;
|
|
skb = brcmu_pkt_buf_get_skb(len);
|
|
skb = brcmu_pkt_buf_get_skb(len);
|
|
@@ -884,6 +922,8 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
|
|
skcb = brcmf_skbcb(skb);
|
|
skcb = brcmf_skbcb(skb);
|
|
skcb->mac = entry;
|
|
skcb->mac = entry;
|
|
skcb->state = BRCMF_FWS_SKBSTATE_TIM;
|
|
skcb->state = BRCMF_FWS_SKBSTATE_TIM;
|
|
|
|
+ skcb->htod = 0;
|
|
|
|
+ skcb->htod_seq = 0;
|
|
bus = fws->drvr->bus_if;
|
|
bus = fws->drvr->bus_if;
|
|
err = brcmf_fws_hdrpush(fws, skb);
|
|
err = brcmf_fws_hdrpush(fws, skb);
|
|
if (err == 0) {
|
|
if (err == 0) {
|
|
@@ -1172,8 +1212,13 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws,
|
|
{
|
|
{
|
|
int prec = 2 * fifo;
|
|
int prec = 2 * fifo;
|
|
u32 *qfull_stat = &fws->stats.delayq_full_error;
|
|
u32 *qfull_stat = &fws->stats.delayq_full_error;
|
|
-
|
|
|
|
struct brcmf_fws_mac_descriptor *entry;
|
|
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;
|
|
entry = brcmf_skbcb(p)->mac;
|
|
if (entry == NULL) {
|
|
if (entry == NULL) {
|
|
@@ -1185,9 +1230,55 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws,
|
|
if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
|
|
if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
|
|
prec += 1;
|
|
prec += 1;
|
|
qfull_stat = &fws->stats.supprq_full_error;
|
|
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;
|
|
*qfull_stat += 1;
|
|
return -ENFILE;
|
|
return -ENFILE;
|
|
}
|
|
}
|
|
@@ -1277,7 +1368,8 @@ done:
|
|
}
|
|
}
|
|
|
|
|
|
static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
|
|
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;
|
|
struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
|
|
u32 hslot;
|
|
u32 hslot;
|
|
@@ -1298,6 +1390,14 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
|
|
|
|
|
|
ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
|
|
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,
|
|
ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo,
|
|
skb);
|
|
skb);
|
|
if (ret != 0) {
|
|
if (ret != 0) {
|
|
@@ -1317,7 +1417,7 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
|
|
|
|
|
|
static int
|
|
static int
|
|
brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
|
|
brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
|
|
- u32 genbit)
|
|
|
|
|
|
+ u32 genbit, u16 seq)
|
|
{
|
|
{
|
|
u32 fifo;
|
|
u32 fifo;
|
|
int ret;
|
|
int ret;
|
|
@@ -1360,8 +1460,8 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
|
|
if (entry->suppressed && entry->suppr_transit_count)
|
|
if (entry->suppressed && entry->suppr_transit_count)
|
|
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 */
|
|
/* pick up the implicit credit from this packet */
|
|
fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
|
|
fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
|
|
@@ -1374,7 +1474,8 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
|
|
brcmf_fws_macdesc_return_req_credit(skb);
|
|
brcmf_fws_macdesc_return_req_credit(skb);
|
|
|
|
|
|
if (!remove_from_hanger)
|
|
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)
|
|
if (remove_from_hanger || ret)
|
|
brcmf_txfinalize(fws->drvr, skb, true);
|
|
brcmf_txfinalize(fws->drvr, skb, true);
|
|
@@ -1406,10 +1507,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)
|
|
static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
|
|
{
|
|
{
|
|
__le32 status_le;
|
|
__le32 status_le;
|
|
|
|
+ __le16 seq_le;
|
|
u32 status;
|
|
u32 status;
|
|
u32 hslot;
|
|
u32 hslot;
|
|
u32 genbit;
|
|
u32 genbit;
|
|
u8 flags;
|
|
u8 flags;
|
|
|
|
+ u16 seq;
|
|
|
|
|
|
fws->stats.txs_indicate++;
|
|
fws->stats.txs_indicate++;
|
|
memcpy(&status_le, data, sizeof(status_le));
|
|
memcpy(&status_le, data, sizeof(status_le));
|
|
@@ -1417,9 +1520,16 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
|
|
flags = brcmf_txstatus_get_field(status, FLAGS);
|
|
flags = brcmf_txstatus_get_field(status, FLAGS);
|
|
hslot = brcmf_txstatus_get_field(status, HSLOT);
|
|
hslot = brcmf_txstatus_get_field(status, HSLOT);
|
|
genbit = brcmf_txstatus_get_field(status, GENERATION);
|
|
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_lock(fws);
|
|
- brcmf_fws_txs_process(fws, flags, hslot, genbit);
|
|
|
|
|
|
+ brcmf_fws_txs_process(fws, flags, hslot, genbit, seq);
|
|
brcmf_fws_unlock(fws);
|
|
brcmf_fws_unlock(fws);
|
|
return BRCMF_FWS_RET_OK_NOSCHEDULE;
|
|
return BRCMF_FWS_RET_OK_NOSCHEDULE;
|
|
}
|
|
}
|
|
@@ -1610,8 +1720,8 @@ static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
|
|
struct brcmf_fws_mac_descriptor *entry = skcb->mac;
|
|
struct brcmf_fws_mac_descriptor *entry = skcb->mac;
|
|
u8 flags;
|
|
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;
|
|
flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
|
|
if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
|
|
if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
|
|
/*
|
|
/*
|
|
@@ -1652,7 +1762,7 @@ static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
|
|
fws->stats.rollback_failed++;
|
|
fws->stats.rollback_failed++;
|
|
hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
|
|
hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
|
|
brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
|
|
brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
|
|
- hslot, 0);
|
|
|
|
|
|
+ hslot, 0, 0);
|
|
} else {
|
|
} else {
|
|
fws->stats.rollback_success++;
|
|
fws->stats.rollback_success++;
|
|
brcmf_fws_return_credits(fws, fifo, 1);
|
|
brcmf_fws_return_credits(fws, fifo, 1);
|
|
@@ -1732,6 +1842,8 @@ static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,
|
|
struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
|
|
struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
|
|
int rc, hslot;
|
|
int rc, hslot;
|
|
|
|
|
|
|
|
+ skcb->htod = 0;
|
|
|
|
+ skcb->htod_seq = 0;
|
|
hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
|
|
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, HSLOT, hslot);
|
|
brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);
|
|
brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);
|
|
@@ -1908,6 +2020,7 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
|
|
struct brcmf_fws_info *fws;
|
|
struct brcmf_fws_info *fws;
|
|
u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
|
|
u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
|
|
int rc;
|
|
int rc;
|
|
|
|
+ u32 mode;
|
|
|
|
|
|
drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
|
|
drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
|
|
if (!drvr->fws) {
|
|
if (!drvr->fws) {
|
|
@@ -1966,6 +2079,18 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
|
|
if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
|
|
if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
|
|
brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
|
|
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_hanger_init(&fws->hanger);
|
|
brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
|
|
brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
|
|
brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
|
|
brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
|
|
@@ -2022,7 +2147,7 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
|
|
}
|
|
}
|
|
brcmf_fws_lock(fws);
|
|
brcmf_fws_lock(fws);
|
|
hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
|
|
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);
|
|
brcmf_fws_unlock(fws);
|
|
}
|
|
}
|
|
|
|
|