Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c56caa9d authored by Franky Lin's avatar Franky Lin Committed by Kalle Valo
Browse files

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: 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>
parent 2aec2c9d
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -216,7 +216,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, struct brcmf_mp_device *settings);
int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings);
+36 −10
Original line number Original line Diff line number Diff line
@@ -311,15 +311,16 @@ 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 */
	if (handle_event)
		brcmf_fweh_process_skb(ifp->drvr, skb);
		brcmf_fweh_process_skb(ifp->drvr, skb);


	if (!(ifp->ndev->flags & IFF_UP)) {
	if (!(ifp->ndev->flags & IFF_UP)) {
@@ -381,7 +382,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;
	}
	}


@@ -393,7 +394,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;
		}
		}


@@ -418,7 +419,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;
		}
		}


@@ -532,11 +533,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);
@@ -560,7 +561,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)
+2 −1
Original line number Original line Diff line number Diff line
@@ -225,7 +225,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);
int __init brcmf_core_init(void);
int __init brcmf_core_init(void);
void __exit brcmf_core_exit(void);
void __exit brcmf_core_exit(void);
+24 −18
Original line number Original line Diff line number Diff line
@@ -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>
@@ -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)
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);
@@ -1116,7 +1102,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);
}
}




@@ -1128,6 +1126,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);


@@ -1148,7 +1147,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);
}
}




+24 −8
Original line number Original line Diff line number Diff line
@@ -1294,6 +1294,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)
@@ -1641,7 +1652,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++;
		}
		}


@@ -1967,18 +1982,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;
Loading