brcmfmac: screening firmware event packet
authorFranky Lin <franky.lin@broadcom.com>
Mon, 11 Apr 2016 09:35:25 +0000 (11:35 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Thu, 14 Apr 2016 12:57:50 +0000 (15:57 +0300)
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>
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c

index 8e02a478e889602a61fa4f07ad260d20bea9182b..31856eb57bc48e82d680fc3b20f05ad85283b33d 100644 (file)
@@ -216,7 +216,9 @@ bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt,
                      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);
index ff825cd7739e29e946cb1120aeaf06b1ecfbbcdb..8a91a517478bf86c2bb844afbd977b64af5a166c 100644 (file)
@@ -311,16 +311,17 @@ void brcmf_txflowblock(struct device *dev, bool state)
        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(struct brcmf_if *ifp, u8 *reorder_data,
        /* 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(struct brcmf_if *ifp, u8 *reorder_data,
                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(struct brcmf_if *ifp, u8 *reorder_data,
                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(struct brcmf_if *ifp, u8 *reorder_data,
 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, struct sk_buff *skb)
        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)
index 7bdb6fef99c3fba9fb3b2970683fd80df0482678..d3497e8bd59c4d2c8d8b7b157540caa55beb488f 100644 (file)
@@ -225,7 +225,8 @@ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
 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);
index 922966734a7f3e8f48e4576b0e6066bc5b3c6fea..3795354b7f1215e60ec4ae482273e9270c72d697 100644 (file)
@@ -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_post(struct brcmf_msgbuf *msgbuf)
 }
 
 
-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(struct brcmf_msgbuf *msgbuf, void *buf)
 
        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 brcmf_msgbuf *msgbuf, void *buf)
        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 brcmf_msgbuf *msgbuf, void *buf)
 
        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);
 }
 
 
index 48d7467d270ee15abf430f5bc85ddac65e55b696..4252fa82b89c64ec8758fde328fbda1754d09aca 100644 (file)
@@ -1294,6 +1294,17 @@ static inline u8 brcmf_sdio_getdatoffset(u8 *swheader)
        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_sdio *bus, u8 rxseq)
                                           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 brcmf_sdio *bus, uint maxframes)
                __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;
index 869eb82db8b1a69c80c55a1fc4c7c702e043866e..aa0b2a192faa5b73a6e25ceff79becd46050dbbd 100644 (file)
@@ -514,7 +514,7 @@ static void brcmf_usb_rx_complete(struct urb *urb)
 
        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);