Commit 5f4688a1 authored by Franky Lin's avatar Franky Lin Committed by Greg Kroah-Hartman

brcmfmac: screening firmware event packet

commit c56caa9d upstream.

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: default avatarPieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
Signed-off-by: default avatarFranky Lin <franky.lin@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
[bwh: Backported to 4.4 adjust filenames]
Signed-off-by: default avatarBen Hutchings <ben.hutchings@codethink.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6da841e9
...@@ -214,7 +214,9 @@ bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, ...@@ -214,7 +214,9 @@ bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt,
int prec); int prec);
/* Receive frame for delivery to OS. Callee disposes of rxp. */ /* 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. */ /* Indication from bus module regarding presence/insertion of dongle. */
int brcmf_attach(struct device *dev); int brcmf_attach(struct device *dev);
......
...@@ -301,16 +301,17 @@ void brcmf_txflowblock(struct device *dev, bool state) ...@@ -301,16 +301,17 @@ void brcmf_txflowblock(struct device *dev, bool state)
brcmf_fws_bus_blocked(drvr, 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, ifp->ndev);
skb->protocol = eth_type_trans(skb, skb->dev);
if (skb->pkt_type == PACKET_MULTICAST) if (skb->pkt_type == PACKET_MULTICAST)
ifp->stats.multicast++; ifp->stats.multicast++;
/* Process special event packets */ /* 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)) { if (!(ifp->ndev->flags & IFF_UP)) {
brcmu_pkt_buf_free_skb(skb); brcmu_pkt_buf_free_skb(skb);
...@@ -371,7 +372,7 @@ static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data, ...@@ -371,7 +372,7 @@ static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
/* validate flags and flow id */ /* validate flags and flow id */
if (flags == 0xFF) { if (flags == 0xFF) {
brcmf_err("invalid flags...so ignore this packet\n"); brcmf_err("invalid flags...so ignore this packet\n");
brcmf_netif_rx(ifp, pkt); brcmf_netif_rx(ifp, pkt, false);
return; return;
} }
...@@ -383,7 +384,7 @@ static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data, ...@@ -383,7 +384,7 @@ static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
if (rfi == NULL) { if (rfi == NULL) {
brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n", brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
flow_id); flow_id);
brcmf_netif_rx(ifp, pkt); brcmf_netif_rx(ifp, pkt, false);
return; return;
} }
...@@ -408,7 +409,7 @@ static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data, ...@@ -408,7 +409,7 @@ static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
rfi = kzalloc(buf_size, GFP_ATOMIC); rfi = kzalloc(buf_size, GFP_ATOMIC);
if (rfi == NULL) { if (rfi == NULL) {
brcmf_err("failed to alloc buffer\n"); brcmf_err("failed to alloc buffer\n");
brcmf_netif_rx(ifp, pkt); brcmf_netif_rx(ifp, pkt, false);
return; return;
} }
...@@ -522,11 +523,11 @@ static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data, ...@@ -522,11 +523,11 @@ static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
netif_rx: netif_rx:
skb_queue_walk_safe(&reorder_list, pkt, pnext) { skb_queue_walk_safe(&reorder_list, pkt, pnext) {
__skb_unlink(pkt, &reorder_list); __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_if *ifp;
struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_bus *bus_if = dev_get_drvdata(dev);
...@@ -550,7 +551,32 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) ...@@ -550,7 +551,32 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
if (rd->reorder) if (rd->reorder)
brcmf_rxreorder_process_info(ifp, rd->reorder, skb); brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
else 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) void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
......
...@@ -215,7 +215,8 @@ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr); ...@@ -215,7 +215,8 @@ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
void brcmf_txflowblock_if(struct brcmf_if *ifp, void brcmf_txflowblock_if(struct brcmf_if *ifp,
enum brcmf_netif_stop_reason reason, bool state); enum brcmf_netif_stop_reason reason, bool state);
void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); 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); void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
#endif /* BRCMFMAC_CORE_H */ #endif /* BRCMFMAC_CORE_H */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <brcmu_utils.h> #include <brcmu_utils.h>
#include <brcmu_wifi.h> #include <brcmu_wifi.h>
...@@ -1076,28 +1077,13 @@ static void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf) ...@@ -1076,28 +1077,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) static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
{ {
struct msgbuf_rx_event *event; struct msgbuf_rx_event *event;
u32 idx; u32 idx;
u16 buflen; u16 buflen;
struct sk_buff *skb; struct sk_buff *skb;
struct brcmf_if *ifp;
event = (struct msgbuf_rx_event *)buf; event = (struct msgbuf_rx_event *)buf;
idx = le32_to_cpu(event->msg.request_id); idx = le32_to_cpu(event->msg.request_id);
...@@ -1117,7 +1103,19 @@ static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf) ...@@ -1117,7 +1103,19 @@ static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
skb_trim(skb, buflen); 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);
} }
...@@ -1129,6 +1127,7 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) ...@@ -1129,6 +1127,7 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf)
u16 data_offset; u16 data_offset;
u16 buflen; u16 buflen;
u32 idx; u32 idx;
struct brcmf_if *ifp;
brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1); brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1);
...@@ -1149,7 +1148,14 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) ...@@ -1149,7 +1148,14 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf)
skb_trim(skb, buflen); 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);
} }
......
...@@ -1394,6 +1394,17 @@ static inline u8 brcmf_sdio_getdatoffset(u8 *swheader) ...@@ -1394,6 +1394,17 @@ static inline u8 brcmf_sdio_getdatoffset(u8 *swheader)
return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT); 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, static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
struct brcmf_sdio_hdrinfo *rd, struct brcmf_sdio_hdrinfo *rd,
enum brcmf_sdio_frmtype type) enum brcmf_sdio_frmtype type)
...@@ -1754,7 +1765,11 @@ static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) ...@@ -1754,7 +1765,11 @@ static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq)
pfirst->len, pfirst->next, pfirst->len, pfirst->next,
pfirst->prev); pfirst->prev);
skb_unlink(pfirst, &bus->glom); 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++; bus->sdcnt.rxglompkts++;
} }
...@@ -2081,18 +2096,19 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) ...@@ -2081,18 +2096,19 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
__skb_trim(pkt, rd->len); __skb_trim(pkt, rd->len);
skb_pull(pkt, rd->dat_offset); 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 */ /* prepare the descriptor for the next read */
rd->len = rd->len_nxtfrm << 4; rd->len = rd->len_nxtfrm << 4;
rd->len_nxtfrm = 0; rd->len_nxtfrm = 0;
/* treat all packet as event if we don't know */ /* treat all packet as event if we don't know */
rd->channel = SDPCM_EVENT_CHANNEL; 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; rxcount = maxframes - rxleft;
......
...@@ -502,7 +502,7 @@ static void brcmf_usb_rx_complete(struct urb *urb) ...@@ -502,7 +502,7 @@ static void brcmf_usb_rx_complete(struct urb *urb)
if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
skb_put(skb, urb->actual_length); 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); brcmf_usb_rx_refill(devinfo, req);
} else { } else {
brcmu_pkt_buf_free_skb(skb); brcmu_pkt_buf_free_skb(skb);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment