diff options
Diffstat (limited to 'package/kernel/mac80211/patches/349-0005-brcmfmac-screening-firmware-event-packet.patch')
-rw-r--r-- | package/kernel/mac80211/patches/349-0005-brcmfmac-screening-firmware-event-packet.patch | 297 |
1 files changed, 297 insertions, 0 deletions
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 index 0000000..4d26404 --- /dev/null +++ b/package/kernel/mac80211/patches/349-0005-brcmfmac-screening-firmware-event-packet.patch @@ -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); |