mac80211: brcmfmac: backport patches that were skipped previously #2
[openwrt/svn-archive/archive.git] / package / kernel / mac80211 / patches / 349-0005-brcmfmac-screening-firmware-event-packet.patch
diff --git a/package/kernel/mac80211/patches/349-0005-brcmfmac-screening-firmware-event-packet.patch b/package/kernel/mac80211/patches/349-0005-brcmfmac-screening-firmware-event-packet.patch
new file mode 100644 (file)
index 0000000..4d26404
--- /dev/null
@@ -0,0 +1,297 @@
+From: Franky Lin <franky.lin@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:25 +0200
+Subject: [PATCH] brcmfmac: screening firmware event packet
+
+Firmware uses asynchronized events as a communication method to the
+host. The event packets are marked as ETH_P_LINK_CTL protocol type. For
+SDIO and PCIe bus, this kind of packets are delivered through virtual
+event channel not data channel. This patch adds a screening logic to
+make sure the event handler only processes the events coming from the
+correct channel.
+
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Signed-off-by: Franky Lin <franky.lin@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+@@ -216,7 +216,9 @@ bool brcmf_c_prec_enq(struct device *dev
+                     int prec);
+ /* Receive frame for delivery to OS.  Callee disposes of rxp. */
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp);
++void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp, bool handle_evnt);
++/* Receive async event packet from firmware. Callee disposes of rxp. */
++void brcmf_rx_event(struct device *dev, struct sk_buff *rxp);
+ /* Indication from bus module regarding presence/insertion of dongle. */
+ int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -311,16 +311,17 @@ void brcmf_txflowblock(struct device *de
+       brcmf_fws_bus_blocked(drvr, state);
+ }
+-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
++void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb,
++                  bool handle_event)
+ {
+-      skb->dev = ifp->ndev;
+-      skb->protocol = eth_type_trans(skb, skb->dev);
++      skb->protocol = eth_type_trans(skb, ifp->ndev);
+       if (skb->pkt_type == PACKET_MULTICAST)
+               ifp->stats.multicast++;
+       /* Process special event packets */
+-      brcmf_fweh_process_skb(ifp->drvr, skb);
++      if (handle_event)
++              brcmf_fweh_process_skb(ifp->drvr, skb);
+       if (!(ifp->ndev->flags & IFF_UP)) {
+               brcmu_pkt_buf_free_skb(skb);
+@@ -381,7 +382,7 @@ static void brcmf_rxreorder_process_info
+       /* validate flags and flow id */
+       if (flags == 0xFF) {
+               brcmf_err("invalid flags...so ignore this packet\n");
+-              brcmf_netif_rx(ifp, pkt);
++              brcmf_netif_rx(ifp, pkt, false);
+               return;
+       }
+@@ -393,7 +394,7 @@ static void brcmf_rxreorder_process_info
+               if (rfi == NULL) {
+                       brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+                                 flow_id);
+-                      brcmf_netif_rx(ifp, pkt);
++                      brcmf_netif_rx(ifp, pkt, false);
+                       return;
+               }
+@@ -418,7 +419,7 @@ static void brcmf_rxreorder_process_info
+               rfi = kzalloc(buf_size, GFP_ATOMIC);
+               if (rfi == NULL) {
+                       brcmf_err("failed to alloc buffer\n");
+-                      brcmf_netif_rx(ifp, pkt);
++                      brcmf_netif_rx(ifp, pkt, false);
+                       return;
+               }
+@@ -532,11 +533,11 @@ static void brcmf_rxreorder_process_info
+ netif_rx:
+       skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+               __skb_unlink(pkt, &reorder_list);
+-              brcmf_netif_rx(ifp, pkt);
++              brcmf_netif_rx(ifp, pkt, false);
+       }
+ }
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
++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);
+@@ -560,7 +561,32 @@ void brcmf_rx_frame(struct device *dev,
+       if (rd->reorder)
+               brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
+       else
+-              brcmf_netif_rx(ifp, skb);
++              brcmf_netif_rx(ifp, skb, handle_evnt);
++}
++
++void brcmf_rx_event(struct device *dev, struct sk_buff *skb)
++{
++      struct brcmf_if *ifp;
++      struct brcmf_bus *bus_if = dev_get_drvdata(dev);
++      struct brcmf_pub *drvr = bus_if->drvr;
++      int ret;
++
++      brcmf_dbg(EVENT, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
++
++      /* process and remove protocol-specific header */
++      ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
++
++      if (ret || !ifp || !ifp->ndev) {
++              if (ret != -ENODATA && ifp)
++                      ifp->stats.rx_errors++;
++              brcmu_pkt_buf_free_skb(skb);
++              return;
++      }
++
++      skb->protocol = eth_type_trans(skb, ifp->ndev);
++
++      brcmf_fweh_process_skb(ifp->drvr, skb);
++      brcmu_pkt_buf_free_skb(skb);
+ }
+ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+@@ -225,7 +225,8 @@ int brcmf_get_next_free_bsscfgidx(struct
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+                         enum brcmf_netif_stop_reason reason, bool state);
+ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
+-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
++void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb,
++                  bool handle_event);
+ void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
+ int __init brcmf_core_init(void);
+ void __exit brcmf_core_exit(void);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+@@ -20,6 +20,7 @@
+ #include <linux/types.h>
+ #include <linux/netdevice.h>
++#include <linux/etherdevice.h>
+ #include <brcmu_utils.h>
+ #include <brcmu_wifi.h>
+@@ -1075,28 +1076,13 @@ static void brcmf_msgbuf_rxbuf_event_pos
+ }
+-static void
+-brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb,
+-                  u8 ifidx)
+-{
+-      struct brcmf_if *ifp;
+-
+-      ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
+-      if (!ifp || !ifp->ndev) {
+-              brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
+-              brcmu_pkt_buf_free_skb(skb);
+-              return;
+-      }
+-      brcmf_netif_rx(ifp, skb);
+-}
+-
+-
+ static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
+ {
+       struct msgbuf_rx_event *event;
+       u32 idx;
+       u16 buflen;
+       struct sk_buff *skb;
++      struct brcmf_if *ifp;
+       event = (struct msgbuf_rx_event *)buf;
+       idx = le32_to_cpu(event->msg.request_id);
+@@ -1116,7 +1102,19 @@ static void brcmf_msgbuf_process_event(s
+       skb_trim(skb, buflen);
+-      brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx);
++      ifp = brcmf_get_ifp(msgbuf->drvr, event->msg.ifidx);
++      if (!ifp || !ifp->ndev) {
++              brcmf_err("Received pkt for invalid ifidx %d\n",
++                        event->msg.ifidx);
++              goto exit;
++      }
++
++      skb->protocol = eth_type_trans(skb, ifp->ndev);
++
++      brcmf_fweh_process_skb(ifp->drvr, skb);
++
++exit:
++      brcmu_pkt_buf_free_skb(skb);
+ }
+@@ -1128,6 +1126,7 @@ brcmf_msgbuf_process_rx_complete(struct
+       u16 data_offset;
+       u16 buflen;
+       u32 idx;
++      struct brcmf_if *ifp;
+       brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1);
+@@ -1148,7 +1147,14 @@ brcmf_msgbuf_process_rx_complete(struct
+       skb_trim(skb, buflen);
+-      brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx);
++      ifp = brcmf_get_ifp(msgbuf->drvr, rx_complete->msg.ifidx);
++      if (!ifp || !ifp->ndev) {
++              brcmf_err("Received pkt for invalid ifidx %d\n",
++                        rx_complete->msg.ifidx);
++              brcmu_pkt_buf_free_skb(skb);
++              return;
++      }
++      brcmf_netif_rx(ifp, skb, false);
+ }
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+@@ -1294,6 +1294,17 @@ static inline u8 brcmf_sdio_getdatoffset
+       return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
+ }
++static inline bool brcmf_sdio_fromevntchan(u8 *swheader)
++{
++      u32 hdrvalue;
++      u8 ret;
++
++      hdrvalue = *(u32 *)swheader;
++      ret = (u8)((hdrvalue & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT);
++
++      return (ret == SDPCM_EVENT_CHANNEL);
++}
++
+ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
+                             struct brcmf_sdio_hdrinfo *rd,
+                             enum brcmf_sdio_frmtype type)
+@@ -1641,7 +1652,11 @@ static u8 brcmf_sdio_rxglom(struct brcmf
+                                          pfirst->len, pfirst->next,
+                                          pfirst->prev);
+                       skb_unlink(pfirst, &bus->glom);
+-                      brcmf_rx_frame(bus->sdiodev->dev, pfirst);
++                      if (brcmf_sdio_fromevntchan(pfirst->data))
++                              brcmf_rx_event(bus->sdiodev->dev, pfirst);
++                      else
++                              brcmf_rx_frame(bus->sdiodev->dev, pfirst,
++                                             false);
+                       bus->sdcnt.rxglompkts++;
+               }
+@@ -1967,18 +1982,19 @@ static uint brcmf_sdio_readframes(struct
+               __skb_trim(pkt, rd->len);
+               skb_pull(pkt, rd->dat_offset);
++              if (pkt->len == 0)
++                      brcmu_pkt_buf_free_skb(pkt);
++              else if (rd->channel == SDPCM_EVENT_CHANNEL)
++                      brcmf_rx_event(bus->sdiodev->dev, pkt);
++              else
++                      brcmf_rx_frame(bus->sdiodev->dev, pkt,
++                                     false);
++
+               /* prepare the descriptor for the next read */
+               rd->len = rd->len_nxtfrm << 4;
+               rd->len_nxtfrm = 0;
+               /* treat all packet as event if we don't know */
+               rd->channel = SDPCM_EVENT_CHANNEL;
+-
+-              if (pkt->len == 0) {
+-                      brcmu_pkt_buf_free_skb(pkt);
+-                      continue;
+-              }
+-
+-              brcmf_rx_frame(bus->sdiodev->dev, pkt);
+       }
+       rxcount = maxframes - rxleft;
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+@@ -514,7 +514,7 @@ static void brcmf_usb_rx_complete(struct
+       if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
+               skb_put(skb, urb->actual_length);
+-              brcmf_rx_frame(devinfo->dev, skb);
++              brcmf_rx_frame(devinfo->dev, skb, true);
+               brcmf_usb_rx_refill(devinfo, req);
+       } else {
+               brcmu_pkt_buf_free_skb(skb);