]>
Commit | Line | Data |
---|---|---|
dd5b528c GKH |
1 | From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST |
2 | From: Franky Lin <franky.lin@broadcom.com> | |
3 | Date: Mon, 11 Apr 2016 11:35:25 +0200 | |
4 | Subject: brcmfmac: screening firmware event packet | |
5 | ||
6 | From: Franky Lin <franky.lin@broadcom.com> | |
7 | ||
8 | commit c56caa9db8abbbfb9e31325e0897705aa897db37 upstream. | |
9 | ||
10 | Firmware uses asynchronized events as a communication method to the | |
11 | host. The event packets are marked as ETH_P_LINK_CTL protocol type. For | |
12 | SDIO and PCIe bus, this kind of packets are delivered through virtual | |
13 | event channel not data channel. This patch adds a screening logic to | |
14 | make sure the event handler only processes the events coming from the | |
15 | correct channel. | |
16 | ||
17 | Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | |
18 | Signed-off-by: Franky Lin <franky.lin@broadcom.com> | |
19 | Signed-off-by: Arend van Spriel <arend@broadcom.com> | |
20 | Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | |
21 | [bwh: Backported to 4.4 adjust filenames] | |
22 | Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk> | |
23 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
24 | --- | |
25 | drivers/net/wireless/brcm80211/brcmfmac/bus.h | 4 +- | |
26 | drivers/net/wireless/brcm80211/brcmfmac/core.c | 46 ++++++++++++++++++----- | |
27 | drivers/net/wireless/brcm80211/brcmfmac/core.h | 3 + | |
28 | drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 42 ++++++++++++--------- | |
29 | drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 32 ++++++++++++---- | |
30 | drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 - | |
31 | 6 files changed, 90 insertions(+), 39 deletions(-) | |
32 | ||
33 | --- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h | |
34 | +++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h | |
35 | @@ -214,7 +214,9 @@ bool brcmf_c_prec_enq(struct device *dev | |
36 | int prec); | |
37 | ||
38 | /* Receive frame for delivery to OS. Callee disposes of rxp. */ | |
39 | -void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp); | |
40 | +void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp, bool handle_evnt); | |
41 | +/* Receive async event packet from firmware. Callee disposes of rxp. */ | |
42 | +void brcmf_rx_event(struct device *dev, struct sk_buff *rxp); | |
43 | ||
44 | /* Indication from bus module regarding presence/insertion of dongle. */ | |
45 | int brcmf_attach(struct device *dev); | |
46 | --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c | |
47 | +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c | |
48 | @@ -301,16 +301,17 @@ void brcmf_txflowblock(struct device *de | |
49 | brcmf_fws_bus_blocked(drvr, state); | |
50 | } | |
51 | ||
52 | -void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) | |
53 | +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb, | |
54 | + bool handle_event) | |
55 | { | |
56 | - skb->dev = ifp->ndev; | |
57 | - skb->protocol = eth_type_trans(skb, skb->dev); | |
58 | + skb->protocol = eth_type_trans(skb, ifp->ndev); | |
59 | ||
60 | if (skb->pkt_type == PACKET_MULTICAST) | |
61 | ifp->stats.multicast++; | |
62 | ||
63 | /* Process special event packets */ | |
64 | - brcmf_fweh_process_skb(ifp->drvr, skb); | |
65 | + if (handle_event) | |
66 | + brcmf_fweh_process_skb(ifp->drvr, skb); | |
67 | ||
68 | if (!(ifp->ndev->flags & IFF_UP)) { | |
69 | brcmu_pkt_buf_free_skb(skb); | |
70 | @@ -371,7 +372,7 @@ static void brcmf_rxreorder_process_info | |
71 | /* validate flags and flow id */ | |
72 | if (flags == 0xFF) { | |
73 | brcmf_err("invalid flags...so ignore this packet\n"); | |
74 | - brcmf_netif_rx(ifp, pkt); | |
75 | + brcmf_netif_rx(ifp, pkt, false); | |
76 | return; | |
77 | } | |
78 | ||
79 | @@ -383,7 +384,7 @@ static void brcmf_rxreorder_process_info | |
80 | if (rfi == NULL) { | |
81 | brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n", | |
82 | flow_id); | |
83 | - brcmf_netif_rx(ifp, pkt); | |
84 | + brcmf_netif_rx(ifp, pkt, false); | |
85 | return; | |
86 | } | |
87 | ||
88 | @@ -408,7 +409,7 @@ static void brcmf_rxreorder_process_info | |
89 | rfi = kzalloc(buf_size, GFP_ATOMIC); | |
90 | if (rfi == NULL) { | |
91 | brcmf_err("failed to alloc buffer\n"); | |
92 | - brcmf_netif_rx(ifp, pkt); | |
93 | + brcmf_netif_rx(ifp, pkt, false); | |
94 | return; | |
95 | } | |
96 | ||
97 | @@ -522,11 +523,11 @@ static void brcmf_rxreorder_process_info | |
98 | netif_rx: | |
99 | skb_queue_walk_safe(&reorder_list, pkt, pnext) { | |
100 | __skb_unlink(pkt, &reorder_list); | |
101 | - brcmf_netif_rx(ifp, pkt); | |
102 | + brcmf_netif_rx(ifp, pkt, false); | |
103 | } | |
104 | } | |
105 | ||
106 | -void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) | |
107 | +void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_evnt) | |
108 | { | |
109 | struct brcmf_if *ifp; | |
110 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); | |
111 | @@ -550,7 +551,32 @@ void brcmf_rx_frame(struct device *dev, | |
112 | if (rd->reorder) | |
113 | brcmf_rxreorder_process_info(ifp, rd->reorder, skb); | |
114 | else | |
115 | - brcmf_netif_rx(ifp, skb); | |
116 | + brcmf_netif_rx(ifp, skb, handle_evnt); | |
117 | +} | |
118 | + | |
119 | +void brcmf_rx_event(struct device *dev, struct sk_buff *skb) | |
120 | +{ | |
121 | + struct brcmf_if *ifp; | |
122 | + struct brcmf_bus *bus_if = dev_get_drvdata(dev); | |
123 | + struct brcmf_pub *drvr = bus_if->drvr; | |
124 | + int ret; | |
125 | + | |
126 | + brcmf_dbg(EVENT, "Enter: %s: rxp=%p\n", dev_name(dev), skb); | |
127 | + | |
128 | + /* process and remove protocol-specific header */ | |
129 | + ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp); | |
130 | + | |
131 | + if (ret || !ifp || !ifp->ndev) { | |
132 | + if (ret != -ENODATA && ifp) | |
133 | + ifp->stats.rx_errors++; | |
134 | + brcmu_pkt_buf_free_skb(skb); | |
135 | + return; | |
136 | + } | |
137 | + | |
138 | + skb->protocol = eth_type_trans(skb, ifp->ndev); | |
139 | + | |
140 | + brcmf_fweh_process_skb(ifp->drvr, skb); | |
141 | + brcmu_pkt_buf_free_skb(skb); | |
142 | } | |
143 | ||
144 | void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success) | |
145 | --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h | |
146 | +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h | |
147 | @@ -215,7 +215,8 @@ int brcmf_get_next_free_bsscfgidx(struct | |
148 | void brcmf_txflowblock_if(struct brcmf_if *ifp, | |
149 | enum brcmf_netif_stop_reason reason, bool state); | |
150 | void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); | |
151 | -void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); | |
152 | +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb, | |
153 | + bool handle_event); | |
154 | void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on); | |
155 | ||
156 | #endif /* BRCMFMAC_CORE_H */ | |
157 | --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | |
158 | +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | |
159 | @@ -20,6 +20,7 @@ | |
160 | ||
161 | #include <linux/types.h> | |
162 | #include <linux/netdevice.h> | |
163 | +#include <linux/etherdevice.h> | |
164 | ||
165 | #include <brcmu_utils.h> | |
166 | #include <brcmu_wifi.h> | |
167 | @@ -1076,28 +1077,13 @@ static void brcmf_msgbuf_rxbuf_event_pos | |
168 | } | |
169 | ||
170 | ||
171 | -static void | |
172 | -brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, | |
173 | - u8 ifidx) | |
174 | -{ | |
175 | - struct brcmf_if *ifp; | |
176 | - | |
177 | - ifp = brcmf_get_ifp(msgbuf->drvr, ifidx); | |
178 | - if (!ifp || !ifp->ndev) { | |
179 | - brcmf_err("Received pkt for invalid ifidx %d\n", ifidx); | |
180 | - brcmu_pkt_buf_free_skb(skb); | |
181 | - return; | |
182 | - } | |
183 | - brcmf_netif_rx(ifp, skb); | |
184 | -} | |
185 | - | |
186 | - | |
187 | static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf) | |
188 | { | |
189 | struct msgbuf_rx_event *event; | |
190 | u32 idx; | |
191 | u16 buflen; | |
192 | struct sk_buff *skb; | |
193 | + struct brcmf_if *ifp; | |
194 | ||
195 | event = (struct msgbuf_rx_event *)buf; | |
196 | idx = le32_to_cpu(event->msg.request_id); | |
197 | @@ -1117,7 +1103,19 @@ static void brcmf_msgbuf_process_event(s | |
198 | ||
199 | skb_trim(skb, buflen); | |
200 | ||
201 | - brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx); | |
202 | + ifp = brcmf_get_ifp(msgbuf->drvr, event->msg.ifidx); | |
203 | + if (!ifp || !ifp->ndev) { | |
204 | + brcmf_err("Received pkt for invalid ifidx %d\n", | |
205 | + event->msg.ifidx); | |
206 | + goto exit; | |
207 | + } | |
208 | + | |
209 | + skb->protocol = eth_type_trans(skb, ifp->ndev); | |
210 | + | |
211 | + brcmf_fweh_process_skb(ifp->drvr, skb); | |
212 | + | |
213 | +exit: | |
214 | + brcmu_pkt_buf_free_skb(skb); | |
215 | } | |
216 | ||
217 | ||
218 | @@ -1129,6 +1127,7 @@ brcmf_msgbuf_process_rx_complete(struct | |
219 | u16 data_offset; | |
220 | u16 buflen; | |
221 | u32 idx; | |
222 | + struct brcmf_if *ifp; | |
223 | ||
224 | brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1); | |
225 | ||
226 | @@ -1149,7 +1148,14 @@ brcmf_msgbuf_process_rx_complete(struct | |
227 | ||
228 | skb_trim(skb, buflen); | |
229 | ||
230 | - brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx); | |
231 | + ifp = brcmf_get_ifp(msgbuf->drvr, rx_complete->msg.ifidx); | |
232 | + if (!ifp || !ifp->ndev) { | |
233 | + brcmf_err("Received pkt for invalid ifidx %d\n", | |
234 | + rx_complete->msg.ifidx); | |
235 | + brcmu_pkt_buf_free_skb(skb); | |
236 | + return; | |
237 | + } | |
238 | + brcmf_netif_rx(ifp, skb, false); | |
239 | } | |
240 | ||
241 | ||
242 | --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | |
243 | +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | |
244 | @@ -1394,6 +1394,17 @@ static inline u8 brcmf_sdio_getdatoffset | |
245 | return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT); | |
246 | } | |
247 | ||
248 | +static inline bool brcmf_sdio_fromevntchan(u8 *swheader) | |
249 | +{ | |
250 | + u32 hdrvalue; | |
251 | + u8 ret; | |
252 | + | |
253 | + hdrvalue = *(u32 *)swheader; | |
254 | + ret = (u8)((hdrvalue & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT); | |
255 | + | |
256 | + return (ret == SDPCM_EVENT_CHANNEL); | |
257 | +} | |
258 | + | |
259 | static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, | |
260 | struct brcmf_sdio_hdrinfo *rd, | |
261 | enum brcmf_sdio_frmtype type) | |
262 | @@ -1754,7 +1765,11 @@ static u8 brcmf_sdio_rxglom(struct brcmf | |
263 | pfirst->len, pfirst->next, | |
264 | pfirst->prev); | |
265 | skb_unlink(pfirst, &bus->glom); | |
266 | - brcmf_rx_frame(bus->sdiodev->dev, pfirst); | |
267 | + if (brcmf_sdio_fromevntchan(pfirst->data)) | |
268 | + brcmf_rx_event(bus->sdiodev->dev, pfirst); | |
269 | + else | |
270 | + brcmf_rx_frame(bus->sdiodev->dev, pfirst, | |
271 | + false); | |
272 | bus->sdcnt.rxglompkts++; | |
273 | } | |
274 | ||
275 | @@ -2081,18 +2096,19 @@ static uint brcmf_sdio_readframes(struct | |
276 | __skb_trim(pkt, rd->len); | |
277 | skb_pull(pkt, rd->dat_offset); | |
278 | ||
279 | + if (pkt->len == 0) | |
280 | + brcmu_pkt_buf_free_skb(pkt); | |
281 | + else if (rd->channel == SDPCM_EVENT_CHANNEL) | |
282 | + brcmf_rx_event(bus->sdiodev->dev, pkt); | |
283 | + else | |
284 | + brcmf_rx_frame(bus->sdiodev->dev, pkt, | |
285 | + false); | |
286 | + | |
287 | /* prepare the descriptor for the next read */ | |
288 | rd->len = rd->len_nxtfrm << 4; | |
289 | rd->len_nxtfrm = 0; | |
290 | /* treat all packet as event if we don't know */ | |
291 | rd->channel = SDPCM_EVENT_CHANNEL; | |
292 | - | |
293 | - if (pkt->len == 0) { | |
294 | - brcmu_pkt_buf_free_skb(pkt); | |
295 | - continue; | |
296 | - } | |
297 | - | |
298 | - brcmf_rx_frame(bus->sdiodev->dev, pkt); | |
299 | } | |
300 | ||
301 | rxcount = maxframes - rxleft; | |
302 | --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c | |
303 | +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c | |
304 | @@ -502,7 +502,7 @@ static void brcmf_usb_rx_complete(struct | |
305 | ||
306 | if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { | |
307 | skb_put(skb, urb->actual_length); | |
308 | - brcmf_rx_frame(devinfo->dev, skb); | |
309 | + brcmf_rx_frame(devinfo->dev, skb, true); | |
310 | brcmf_usb_rx_refill(devinfo, req); | |
311 | } else { | |
312 | brcmu_pkt_buf_free_skb(skb); |