|
@@ -0,0 +1,585 @@
|
|
|
|
+From: Arend van Spriel <[email protected]>
|
|
|
|
+Date: Mon, 11 Apr 2016 11:35:26 +0200
|
|
|
|
+Subject: [PATCH] brcmfmac: cleanup ampdu-rx host reorder code
|
|
|
|
+
|
|
|
|
+The code for ampdu-rx host reorder is related to the firmware signalling
|
|
|
|
+supported in BCDC protocol. This change moves the code to fwsignal module.
|
|
|
|
+
|
|
|
|
+Reviewed-by: Hante Meuleman <[email protected]>
|
|
|
|
+Reviewed-by: Pieter-Paul Giesberts <[email protected]>
|
|
|
|
+Reviewed-by: Franky Lin <[email protected]>
|
|
|
|
+Signed-off-by: Arend van Spriel <[email protected]>
|
|
|
|
+Signed-off-by: Kalle Valo <[email protected]>
|
|
|
|
+---
|
|
|
|
+
|
|
|
|
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
|
|
|
|
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
|
|
|
|
+@@ -351,6 +351,12 @@ brcmf_proto_bcdc_add_tdls_peer(struct br
|
|
|
|
+ {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
++static void brcmf_proto_bcdc_rxreorder(struct brcmf_if *ifp,
|
|
|
|
++ struct sk_buff *skb)
|
|
|
|
++{
|
|
|
|
++ brcmf_fws_rxreorder(ifp, skb);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
+ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
|
|
|
|
+ {
|
|
|
|
+ struct brcmf_bcdc *bcdc;
|
|
|
|
+@@ -372,6 +378,7 @@ int brcmf_proto_bcdc_attach(struct brcmf
|
|
|
|
+ drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
|
|
|
|
+ drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
|
|
|
|
+ drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
|
|
|
|
++ drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder;
|
|
|
|
+ drvr->proto->pd = bcdc;
|
|
|
|
+
|
|
|
|
+ drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
|
|
|
|
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
|
|
|
|
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
|
|
|
|
+@@ -40,19 +40,6 @@
|
|
|
|
+
|
|
|
|
+ #define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(950)
|
|
|
|
+
|
|
|
|
+-/* AMPDU rx reordering definitions */
|
|
|
|
+-#define BRCMF_RXREORDER_FLOWID_OFFSET 0
|
|
|
|
+-#define BRCMF_RXREORDER_MAXIDX_OFFSET 2
|
|
|
|
+-#define BRCMF_RXREORDER_FLAGS_OFFSET 4
|
|
|
|
+-#define BRCMF_RXREORDER_CURIDX_OFFSET 6
|
|
|
|
+-#define BRCMF_RXREORDER_EXPIDX_OFFSET 8
|
|
|
|
+-
|
|
|
|
+-#define BRCMF_RXREORDER_DEL_FLOW 0x01
|
|
|
|
+-#define BRCMF_RXREORDER_FLUSH_ALL 0x02
|
|
|
|
+-#define BRCMF_RXREORDER_CURIDX_VALID 0x04
|
|
|
|
+-#define BRCMF_RXREORDER_EXPIDX_VALID 0x08
|
|
|
|
+-#define BRCMF_RXREORDER_NEW_HOLE 0x10
|
|
|
|
+-
|
|
|
|
+ #define BRCMF_BSSIDX_INVALID -1
|
|
|
|
+
|
|
|
|
+ char *brcmf_ifname(struct brcmf_if *ifp)
|
|
|
|
+@@ -342,207 +329,11 @@ void brcmf_netif_rx(struct brcmf_if *ifp
|
|
|
|
+ netif_rx_ni(skb);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+-static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
|
|
|
|
+- u8 start, u8 end,
|
|
|
|
+- struct sk_buff_head *skb_list)
|
|
|
|
+-{
|
|
|
|
+- /* initialize return list */
|
|
|
|
+- __skb_queue_head_init(skb_list);
|
|
|
|
+-
|
|
|
|
+- if (rfi->pend_pkts == 0) {
|
|
|
|
+- brcmf_dbg(INFO, "no packets in reorder queue\n");
|
|
|
|
+- return;
|
|
|
|
+- }
|
|
|
|
+-
|
|
|
|
+- do {
|
|
|
|
+- if (rfi->pktslots[start]) {
|
|
|
|
+- __skb_queue_tail(skb_list, rfi->pktslots[start]);
|
|
|
|
+- rfi->pktslots[start] = NULL;
|
|
|
|
+- }
|
|
|
|
+- start++;
|
|
|
|
+- if (start > rfi->max_idx)
|
|
|
|
+- start = 0;
|
|
|
|
+- } while (start != end);
|
|
|
|
+- rfi->pend_pkts -= skb_queue_len(skb_list);
|
|
|
|
+-}
|
|
|
|
+-
|
|
|
|
+-static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
|
|
|
|
+- struct sk_buff *pkt)
|
|
|
|
+-{
|
|
|
|
+- u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
|
|
|
|
+- struct brcmf_ampdu_rx_reorder *rfi;
|
|
|
|
+- struct sk_buff_head reorder_list;
|
|
|
|
+- struct sk_buff *pnext;
|
|
|
|
+- u8 flags;
|
|
|
|
+- u32 buf_size;
|
|
|
|
+-
|
|
|
|
+- flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
|
|
|
|
+- flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
|
|
|
|
+-
|
|
|
|
+- /* validate flags and flow id */
|
|
|
|
+- if (flags == 0xFF) {
|
|
|
|
+- brcmf_err("invalid flags...so ignore this packet\n");
|
|
|
|
+- brcmf_netif_rx(ifp, pkt, false);
|
|
|
|
+- return;
|
|
|
|
+- }
|
|
|
|
+-
|
|
|
|
+- rfi = ifp->drvr->reorder_flows[flow_id];
|
|
|
|
+- if (flags & BRCMF_RXREORDER_DEL_FLOW) {
|
|
|
|
+- brcmf_dbg(INFO, "flow-%d: delete\n",
|
|
|
|
+- flow_id);
|
|
|
|
+-
|
|
|
|
+- if (rfi == NULL) {
|
|
|
|
+- brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
|
|
|
|
+- flow_id);
|
|
|
|
+- brcmf_netif_rx(ifp, pkt, false);
|
|
|
|
+- return;
|
|
|
|
+- }
|
|
|
|
+-
|
|
|
|
+- brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
|
|
|
|
+- &reorder_list);
|
|
|
|
+- /* add the last packet */
|
|
|
|
+- __skb_queue_tail(&reorder_list, pkt);
|
|
|
|
+- kfree(rfi);
|
|
|
|
+- ifp->drvr->reorder_flows[flow_id] = NULL;
|
|
|
|
+- goto netif_rx;
|
|
|
|
+- }
|
|
|
|
+- /* from here on we need a flow reorder instance */
|
|
|
|
+- if (rfi == NULL) {
|
|
|
|
+- buf_size = sizeof(*rfi);
|
|
|
|
+- max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
|
|
|
|
+-
|
|
|
|
+- buf_size += (max_idx + 1) * sizeof(pkt);
|
|
|
|
+-
|
|
|
|
+- /* allocate space for flow reorder info */
|
|
|
|
+- brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
|
|
|
|
+- flow_id, max_idx);
|
|
|
|
+- rfi = kzalloc(buf_size, GFP_ATOMIC);
|
|
|
|
+- if (rfi == NULL) {
|
|
|
|
+- brcmf_err("failed to alloc buffer\n");
|
|
|
|
+- brcmf_netif_rx(ifp, pkt, false);
|
|
|
|
+- return;
|
|
|
|
+- }
|
|
|
|
+-
|
|
|
|
+- ifp->drvr->reorder_flows[flow_id] = rfi;
|
|
|
|
+- rfi->pktslots = (struct sk_buff **)(rfi+1);
|
|
|
|
+- rfi->max_idx = max_idx;
|
|
|
|
+- }
|
|
|
|
+- if (flags & BRCMF_RXREORDER_NEW_HOLE) {
|
|
|
|
+- if (rfi->pend_pkts) {
|
|
|
|
+- brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
|
|
|
|
+- rfi->exp_idx,
|
|
|
|
+- &reorder_list);
|
|
|
|
+- WARN_ON(rfi->pend_pkts);
|
|
|
|
+- } else {
|
|
|
|
+- __skb_queue_head_init(&reorder_list);
|
|
|
|
+- }
|
|
|
|
+- rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
|
|
|
|
+- rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
|
|
|
|
+- rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
|
|
|
|
+- rfi->pktslots[rfi->cur_idx] = pkt;
|
|
|
|
+- rfi->pend_pkts++;
|
|
|
|
+- brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
|
|
|
|
+- flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
|
|
|
|
+- } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
|
|
|
|
+- cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
|
|
|
|
+- exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
|
|
|
|
+-
|
|
|
|
+- if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
|
|
|
|
+- /* still in the current hole */
|
|
|
|
+- /* enqueue the current on the buffer chain */
|
|
|
|
+- if (rfi->pktslots[cur_idx] != NULL) {
|
|
|
|
+- brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
|
|
|
|
+- brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
|
|
|
|
+- rfi->pktslots[cur_idx] = NULL;
|
|
|
|
+- }
|
|
|
|
+- rfi->pktslots[cur_idx] = pkt;
|
|
|
|
+- rfi->pend_pkts++;
|
|
|
|
+- rfi->cur_idx = cur_idx;
|
|
|
|
+- brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
|
|
|
|
+- flow_id, cur_idx, exp_idx, rfi->pend_pkts);
|
|
|
|
+-
|
|
|
|
+- /* can return now as there is no reorder
|
|
|
|
+- * list to process.
|
|
|
|
+- */
|
|
|
|
+- return;
|
|
|
|
+- }
|
|
|
|
+- if (rfi->exp_idx == cur_idx) {
|
|
|
|
+- if (rfi->pktslots[cur_idx] != NULL) {
|
|
|
|
+- brcmf_dbg(INFO, "error buffer pending..free it\n");
|
|
|
|
+- brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
|
|
|
|
+- rfi->pktslots[cur_idx] = NULL;
|
|
|
|
+- }
|
|
|
|
+- rfi->pktslots[cur_idx] = pkt;
|
|
|
|
+- rfi->pend_pkts++;
|
|
|
|
+-
|
|
|
|
+- /* got the expected one. flush from current to expected
|
|
|
|
+- * and update expected
|
|
|
|
+- */
|
|
|
|
+- brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
|
|
|
|
+- flow_id, cur_idx, exp_idx, rfi->pend_pkts);
|
|
|
|
+-
|
|
|
|
+- rfi->cur_idx = cur_idx;
|
|
|
|
+- rfi->exp_idx = exp_idx;
|
|
|
|
+-
|
|
|
|
+- brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
|
|
|
|
+- &reorder_list);
|
|
|
|
+- brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
|
|
|
|
+- flow_id, skb_queue_len(&reorder_list),
|
|
|
|
+- rfi->pend_pkts);
|
|
|
|
+- } else {
|
|
|
|
+- u8 end_idx;
|
|
|
|
+-
|
|
|
|
+- brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
|
|
|
|
+- flow_id, flags, rfi->cur_idx, rfi->exp_idx,
|
|
|
|
+- cur_idx, exp_idx);
|
|
|
|
+- if (flags & BRCMF_RXREORDER_FLUSH_ALL)
|
|
|
|
+- end_idx = rfi->exp_idx;
|
|
|
|
+- else
|
|
|
|
+- end_idx = exp_idx;
|
|
|
|
+-
|
|
|
|
+- /* flush pkts first */
|
|
|
|
+- brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
|
|
|
|
+- &reorder_list);
|
|
|
|
+-
|
|
|
|
+- if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
|
|
|
|
+- __skb_queue_tail(&reorder_list, pkt);
|
|
|
|
+- } else {
|
|
|
|
+- rfi->pktslots[cur_idx] = pkt;
|
|
|
|
+- rfi->pend_pkts++;
|
|
|
|
+- }
|
|
|
|
+- rfi->exp_idx = exp_idx;
|
|
|
|
+- rfi->cur_idx = cur_idx;
|
|
|
|
+- }
|
|
|
|
+- } else {
|
|
|
|
+- /* explicity window move updating the expected index */
|
|
|
|
+- exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
|
|
|
|
+-
|
|
|
|
+- brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
|
|
|
|
+- flow_id, flags, rfi->exp_idx, exp_idx);
|
|
|
|
+- if (flags & BRCMF_RXREORDER_FLUSH_ALL)
|
|
|
|
+- end_idx = rfi->exp_idx;
|
|
|
|
+- else
|
|
|
|
+- end_idx = exp_idx;
|
|
|
|
+-
|
|
|
|
+- brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
|
|
|
|
+- &reorder_list);
|
|
|
|
+- __skb_queue_tail(&reorder_list, pkt);
|
|
|
|
+- /* set the new expected idx */
|
|
|
|
+- rfi->exp_idx = exp_idx;
|
|
|
|
+- }
|
|
|
|
+-netif_rx:
|
|
|
|
+- skb_queue_walk_safe(&reorder_list, pkt, pnext) {
|
|
|
|
+- __skb_unlink(pkt, &reorder_list);
|
|
|
|
+- brcmf_netif_rx(ifp, pkt, false);
|
|
|
|
+- }
|
|
|
|
+-}
|
|
|
|
+-
|
|
|
|
+ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_evnt)
|
|
|
|
+ {
|
|
|
|
+ struct brcmf_if *ifp;
|
|
|
|
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
|
|
|
|
+ struct brcmf_pub *drvr = bus_if->drvr;
|
|
|
|
+- struct brcmf_skb_reorder_data *rd;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
|
|
|
|
+@@ -557,9 +348,8 @@ void brcmf_rx_frame(struct device *dev,
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+- rd = (struct brcmf_skb_reorder_data *)skb->cb;
|
|
|
|
+- if (rd->reorder)
|
|
|
|
+- brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
|
|
|
|
++ if (brcmf_proto_is_reorder_skb(skb))
|
|
|
|
++ brcmf_proto_rxreorder(ifp, skb);
|
|
|
|
+ else
|
|
|
|
+ brcmf_netif_rx(ifp, skb, handle_evnt);
|
|
|
|
+ }
|
|
|
|
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
|
|
|
|
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
|
|
|
|
+@@ -208,10 +208,6 @@ struct brcmf_if {
|
|
|
|
+ u8 ipv6addr_idx;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+-struct brcmf_skb_reorder_data {
|
|
|
|
+- u8 *reorder;
|
|
|
|
+-};
|
|
|
|
+-
|
|
|
|
+ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
|
|
|
|
+
|
|
|
|
+ /* Return pointer to interface name */
|
|
|
|
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
|
|
|
|
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
|
|
|
|
+@@ -92,6 +92,19 @@ enum brcmf_fws_tlv_len {
|
|
|
|
+ };
|
|
|
|
+ #undef BRCMF_FWS_TLV_DEF
|
|
|
|
+
|
|
|
|
++/* AMPDU rx reordering definitions */
|
|
|
|
++#define BRCMF_RXREORDER_FLOWID_OFFSET 0
|
|
|
|
++#define BRCMF_RXREORDER_MAXIDX_OFFSET 2
|
|
|
|
++#define BRCMF_RXREORDER_FLAGS_OFFSET 4
|
|
|
|
++#define BRCMF_RXREORDER_CURIDX_OFFSET 6
|
|
|
|
++#define BRCMF_RXREORDER_EXPIDX_OFFSET 8
|
|
|
|
++
|
|
|
|
++#define BRCMF_RXREORDER_DEL_FLOW 0x01
|
|
|
|
++#define BRCMF_RXREORDER_FLUSH_ALL 0x02
|
|
|
|
++#define BRCMF_RXREORDER_CURIDX_VALID 0x04
|
|
|
|
++#define BRCMF_RXREORDER_EXPIDX_VALID 0x08
|
|
|
|
++#define BRCMF_RXREORDER_NEW_HOLE 0x10
|
|
|
|
++
|
|
|
|
+ #ifdef DEBUG
|
|
|
|
+ /*
|
|
|
|
+ * brcmf_fws_tlv_names - array of tlv names.
|
|
|
|
+@@ -1614,6 +1627,202 @@ static int brcmf_fws_notify_bcmc_credit_
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
++static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
|
|
|
|
++ u8 start, u8 end,
|
|
|
|
++ struct sk_buff_head *skb_list)
|
|
|
|
++{
|
|
|
|
++ /* initialize return list */
|
|
|
|
++ __skb_queue_head_init(skb_list);
|
|
|
|
++
|
|
|
|
++ if (rfi->pend_pkts == 0) {
|
|
|
|
++ brcmf_dbg(INFO, "no packets in reorder queue\n");
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ do {
|
|
|
|
++ if (rfi->pktslots[start]) {
|
|
|
|
++ __skb_queue_tail(skb_list, rfi->pktslots[start]);
|
|
|
|
++ rfi->pktslots[start] = NULL;
|
|
|
|
++ }
|
|
|
|
++ start++;
|
|
|
|
++ if (start > rfi->max_idx)
|
|
|
|
++ start = 0;
|
|
|
|
++ } while (start != end);
|
|
|
|
++ rfi->pend_pkts -= skb_queue_len(skb_list);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt)
|
|
|
|
++{
|
|
|
|
++ u8 *reorder_data;
|
|
|
|
++ u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
|
|
|
|
++ struct brcmf_ampdu_rx_reorder *rfi;
|
|
|
|
++ struct sk_buff_head reorder_list;
|
|
|
|
++ struct sk_buff *pnext;
|
|
|
|
++ u8 flags;
|
|
|
|
++ u32 buf_size;
|
|
|
|
++
|
|
|
|
++ reorder_data = ((struct brcmf_skb_reorder_data *)pkt->cb)->reorder;
|
|
|
|
++ flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
|
|
|
|
++ flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
|
|
|
|
++
|
|
|
|
++ /* validate flags and flow id */
|
|
|
|
++ if (flags == 0xFF) {
|
|
|
|
++ brcmf_err("invalid flags...so ignore this packet\n");
|
|
|
|
++ brcmf_netif_rx(ifp, pkt, false);
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ rfi = ifp->drvr->reorder_flows[flow_id];
|
|
|
|
++ if (flags & BRCMF_RXREORDER_DEL_FLOW) {
|
|
|
|
++ brcmf_dbg(INFO, "flow-%d: delete\n",
|
|
|
|
++ flow_id);
|
|
|
|
++
|
|
|
|
++ if (rfi == NULL) {
|
|
|
|
++ brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
|
|
|
|
++ flow_id);
|
|
|
|
++ brcmf_netif_rx(ifp, pkt, false);
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
|
|
|
|
++ &reorder_list);
|
|
|
|
++ /* add the last packet */
|
|
|
|
++ __skb_queue_tail(&reorder_list, pkt);
|
|
|
|
++ kfree(rfi);
|
|
|
|
++ ifp->drvr->reorder_flows[flow_id] = NULL;
|
|
|
|
++ goto netif_rx;
|
|
|
|
++ }
|
|
|
|
++ /* from here on we need a flow reorder instance */
|
|
|
|
++ if (rfi == NULL) {
|
|
|
|
++ buf_size = sizeof(*rfi);
|
|
|
|
++ max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
|
|
|
|
++
|
|
|
|
++ buf_size += (max_idx + 1) * sizeof(pkt);
|
|
|
|
++
|
|
|
|
++ /* allocate space for flow reorder info */
|
|
|
|
++ brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
|
|
|
|
++ flow_id, max_idx);
|
|
|
|
++ rfi = kzalloc(buf_size, GFP_ATOMIC);
|
|
|
|
++ if (rfi == NULL) {
|
|
|
|
++ brcmf_err("failed to alloc buffer\n");
|
|
|
|
++ brcmf_netif_rx(ifp, pkt, false);
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ifp->drvr->reorder_flows[flow_id] = rfi;
|
|
|
|
++ rfi->pktslots = (struct sk_buff **)(rfi + 1);
|
|
|
|
++ rfi->max_idx = max_idx;
|
|
|
|
++ }
|
|
|
|
++ if (flags & BRCMF_RXREORDER_NEW_HOLE) {
|
|
|
|
++ if (rfi->pend_pkts) {
|
|
|
|
++ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
|
|
|
|
++ rfi->exp_idx,
|
|
|
|
++ &reorder_list);
|
|
|
|
++ WARN_ON(rfi->pend_pkts);
|
|
|
|
++ } else {
|
|
|
|
++ __skb_queue_head_init(&reorder_list);
|
|
|
|
++ }
|
|
|
|
++ rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
|
|
|
|
++ rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
|
|
|
|
++ rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
|
|
|
|
++ rfi->pktslots[rfi->cur_idx] = pkt;
|
|
|
|
++ rfi->pend_pkts++;
|
|
|
|
++ brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
|
|
|
|
++ flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
|
|
|
|
++ } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
|
|
|
|
++ cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
|
|
|
|
++ exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
|
|
|
|
++
|
|
|
|
++ if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
|
|
|
|
++ /* still in the current hole */
|
|
|
|
++ /* enqueue the current on the buffer chain */
|
|
|
|
++ if (rfi->pktslots[cur_idx] != NULL) {
|
|
|
|
++ brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
|
|
|
|
++ brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
|
|
|
|
++ rfi->pktslots[cur_idx] = NULL;
|
|
|
|
++ }
|
|
|
|
++ rfi->pktslots[cur_idx] = pkt;
|
|
|
|
++ rfi->pend_pkts++;
|
|
|
|
++ rfi->cur_idx = cur_idx;
|
|
|
|
++ brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
|
|
|
|
++ flow_id, cur_idx, exp_idx, rfi->pend_pkts);
|
|
|
|
++
|
|
|
|
++ /* can return now as there is no reorder
|
|
|
|
++ * list to process.
|
|
|
|
++ */
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++ if (rfi->exp_idx == cur_idx) {
|
|
|
|
++ if (rfi->pktslots[cur_idx] != NULL) {
|
|
|
|
++ brcmf_dbg(INFO, "error buffer pending..free it\n");
|
|
|
|
++ brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
|
|
|
|
++ rfi->pktslots[cur_idx] = NULL;
|
|
|
|
++ }
|
|
|
|
++ rfi->pktslots[cur_idx] = pkt;
|
|
|
|
++ rfi->pend_pkts++;
|
|
|
|
++
|
|
|
|
++ /* got the expected one. flush from current to expected
|
|
|
|
++ * and update expected
|
|
|
|
++ */
|
|
|
|
++ brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
|
|
|
|
++ flow_id, cur_idx, exp_idx, rfi->pend_pkts);
|
|
|
|
++
|
|
|
|
++ rfi->cur_idx = cur_idx;
|
|
|
|
++ rfi->exp_idx = exp_idx;
|
|
|
|
++
|
|
|
|
++ brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
|
|
|
|
++ &reorder_list);
|
|
|
|
++ brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
|
|
|
|
++ flow_id, skb_queue_len(&reorder_list),
|
|
|
|
++ rfi->pend_pkts);
|
|
|
|
++ } else {
|
|
|
|
++ u8 end_idx;
|
|
|
|
++
|
|
|
|
++ brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
|
|
|
|
++ flow_id, flags, rfi->cur_idx, rfi->exp_idx,
|
|
|
|
++ cur_idx, exp_idx);
|
|
|
|
++ if (flags & BRCMF_RXREORDER_FLUSH_ALL)
|
|
|
|
++ end_idx = rfi->exp_idx;
|
|
|
|
++ else
|
|
|
|
++ end_idx = exp_idx;
|
|
|
|
++
|
|
|
|
++ /* flush pkts first */
|
|
|
|
++ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
|
|
|
|
++ &reorder_list);
|
|
|
|
++
|
|
|
|
++ if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
|
|
|
|
++ __skb_queue_tail(&reorder_list, pkt);
|
|
|
|
++ } else {
|
|
|
|
++ rfi->pktslots[cur_idx] = pkt;
|
|
|
|
++ rfi->pend_pkts++;
|
|
|
|
++ }
|
|
|
|
++ rfi->exp_idx = exp_idx;
|
|
|
|
++ rfi->cur_idx = cur_idx;
|
|
|
|
++ }
|
|
|
|
++ } else {
|
|
|
|
++ /* explicity window move updating the expected index */
|
|
|
|
++ exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
|
|
|
|
++
|
|
|
|
++ brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
|
|
|
|
++ flow_id, flags, rfi->exp_idx, exp_idx);
|
|
|
|
++ if (flags & BRCMF_RXREORDER_FLUSH_ALL)
|
|
|
|
++ end_idx = rfi->exp_idx;
|
|
|
|
++ else
|
|
|
|
++ end_idx = exp_idx;
|
|
|
|
++
|
|
|
|
++ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
|
|
|
|
++ &reorder_list);
|
|
|
|
++ __skb_queue_tail(&reorder_list, pkt);
|
|
|
|
++ /* set the new expected idx */
|
|
|
|
++ rfi->exp_idx = exp_idx;
|
|
|
|
++ }
|
|
|
|
++netif_rx:
|
|
|
|
++ skb_queue_walk_safe(&reorder_list, pkt, pnext) {
|
|
|
|
++ __skb_unlink(pkt, &reorder_list);
|
|
|
|
++ brcmf_netif_rx(ifp, pkt, false);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
+ void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
|
|
|
|
+ {
|
|
|
|
+ struct brcmf_skb_reorder_data *rd;
|
|
|
|
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
|
|
|
|
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
|
|
|
|
+@@ -29,5 +29,6 @@ void brcmf_fws_add_interface(struct brcm
|
|
|
|
+ void brcmf_fws_del_interface(struct brcmf_if *ifp);
|
|
|
|
+ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb);
|
|
|
|
+ void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked);
|
|
|
|
++void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb);
|
|
|
|
+
|
|
|
|
+ #endif /* FWSIGNAL_H_ */
|
|
|
|
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
|
|
|
|
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
|
|
|
|
+@@ -527,6 +527,9 @@ static int brcmf_msgbuf_hdrpull(struct b
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
++static void brcmf_msgbuf_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb)
|
|
|
|
++{
|
|
|
|
++}
|
|
|
|
+
|
|
|
|
+ static void
|
|
|
|
+ brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid)
|
|
|
|
+@@ -1466,6 +1469,7 @@ int brcmf_proto_msgbuf_attach(struct brc
|
|
|
|
+ drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode;
|
|
|
|
+ drvr->proto->delete_peer = brcmf_msgbuf_delete_peer;
|
|
|
|
+ drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer;
|
|
|
|
++ drvr->proto->rxreorder = brcmf_msgbuf_rxreorder;
|
|
|
|
+ drvr->proto->pd = msgbuf;
|
|
|
|
+
|
|
|
|
+ init_waitqueue_head(&msgbuf->ioctl_resp_wait);
|
|
|
|
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
|
|
|
|
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
|
|
|
|
+@@ -22,6 +22,9 @@ enum proto_addr_mode {
|
|
|
|
+ ADDR_DIRECT
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
++struct brcmf_skb_reorder_data {
|
|
|
|
++ u8 *reorder;
|
|
|
|
++};
|
|
|
|
+
|
|
|
|
+ struct brcmf_proto {
|
|
|
|
+ int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws,
|
|
|
|
+@@ -38,6 +41,7 @@ struct brcmf_proto {
|
|
|
|
+ u8 peer[ETH_ALEN]);
|
|
|
|
+ void (*add_tdls_peer)(struct brcmf_pub *drvr, int ifidx,
|
|
|
|
+ u8 peer[ETH_ALEN]);
|
|
|
|
++ void (*rxreorder)(struct brcmf_if *ifp, struct sk_buff *skb);
|
|
|
|
+ void *pd;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+@@ -91,6 +95,18 @@ brcmf_proto_add_tdls_peer(struct brcmf_p
|
|
|
|
+ {
|
|
|
|
+ drvr->proto->add_tdls_peer(drvr, ifidx, peer);
|
|
|
|
+ }
|
|
|
|
++static inline bool brcmf_proto_is_reorder_skb(struct sk_buff *skb)
|
|
|
|
++{
|
|
|
|
++ struct brcmf_skb_reorder_data *rd;
|
|
|
|
++
|
|
|
|
++ rd = (struct brcmf_skb_reorder_data *)skb->cb;
|
|
|
|
++ return !!rd->reorder;
|
|
|
|
++}
|
|
|
|
+
|
|
|
|
++static inline void
|
|
|
|
++brcmf_proto_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb)
|
|
|
|
++{
|
|
|
|
++ ifp->drvr->proto->rxreorder(ifp, skb);
|
|
|
|
++}
|
|
|
|
+
|
|
|
|
+ #endif /* BRCMFMAC_PROTO_H */
|