xref: /linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c (revision 26fbb4c8c7c3ee9a4c3b4de555a8587b5a19154e)
1 // SPDX-License-Identifier: ISC
2 /*
3  * Copyright (c) 2010 Broadcom Corporation
4  */
5 
6 /*******************************************************************************
7  * Communicates with the dongle by using dcmd codes.
8  * For certain dcmd codes, the dongle interprets string data from the host.
9  ******************************************************************************/
10 
11 #include <linux/types.h>
12 #include <linux/netdevice.h>
13 
14 #include <brcmu_utils.h>
15 #include <brcmu_wifi.h>
16 
17 #include "core.h"
18 #include "bus.h"
19 #include "fwsignal.h"
20 #include "debug.h"
21 #include "tracepoint.h"
22 #include "proto.h"
23 #include "bcdc.h"
24 
25 struct brcmf_proto_bcdc_dcmd {
26 	__le32 cmd;	/* dongle command value */
27 	__le32 len;	/* lower 16: output buflen;
28 			 * upper 16: input buflen (excludes header) */
29 	__le32 flags;	/* flag defns given below */
30 	__le32 status;	/* status code returned from the device */
31 };
32 
33 /* BCDC flag definitions */
34 #define BCDC_DCMD_ERROR		0x01		/* 1=cmd failed */
35 #define BCDC_DCMD_SET		0x02		/* 0=get, 1=set cmd */
36 #define BCDC_DCMD_IF_MASK	0xF000		/* I/F index */
37 #define BCDC_DCMD_IF_SHIFT	12
38 #define BCDC_DCMD_ID_MASK	0xFFFF0000	/* id an cmd pairing */
39 #define BCDC_DCMD_ID_SHIFT	16		/* ID Mask shift bits */
40 #define BCDC_DCMD_ID(flags)	\
41 	(((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT)
42 
43 /*
44  * BCDC header - Broadcom specific extension of CDC.
45  * Used on data packets to convey priority across USB.
46  */
47 #define	BCDC_HEADER_LEN		4
48 #define BCDC_PROTO_VER		2	/* Protocol version */
49 #define BCDC_FLAG_VER_MASK	0xf0	/* Protocol version mask */
50 #define BCDC_FLAG_VER_SHIFT	4	/* Protocol version shift */
51 #define BCDC_FLAG_SUM_GOOD	0x04	/* Good RX checksums */
52 #define BCDC_FLAG_SUM_NEEDED	0x08	/* Dongle needs to do TX checksums */
53 #define BCDC_PRIORITY_MASK	0x7
54 #define BCDC_FLAG2_IF_MASK	0x0f	/* packet rx interface in APSTA */
55 #define BCDC_FLAG2_IF_SHIFT	0
56 
57 #define BCDC_GET_IF_IDX(hdr) \
58 	((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT))
59 #define BCDC_SET_IF_IDX(hdr, idx) \
60 	((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \
61 	((idx) << BCDC_FLAG2_IF_SHIFT)))
62 
63 /**
64  * struct brcmf_proto_bcdc_header - BCDC header format
65  *
66  * @flags: flags contain protocol and checksum info.
67  * @priority: 802.1d priority and USB flow control info (bit 4:7).
68  * @flags2: additional flags containing dongle interface index.
69  * @data_offset: start of packet data. header is following by firmware signals.
70  */
71 struct brcmf_proto_bcdc_header {
72 	u8 flags;
73 	u8 priority;
74 	u8 flags2;
75 	u8 data_offset;
76 };
77 
78 /*
79  * maximum length of firmware signal data between
80  * the BCDC header and packet data in the tx path.
81  */
82 #define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES	12
83 
84 #define RETRIES 2 /* # of retries to retrieve matching dcmd response */
85 #define BUS_HEADER_LEN	(16+64)		/* Must be atleast SDPCM_RESERVE
86 					 * (amount of header tha might be added)
87 					 * plus any space that might be needed
88 					 * for bus alignment padding.
89 					 */
90 struct brcmf_bcdc {
91 	u16 reqid;
92 	u8 bus_header[BUS_HEADER_LEN];
93 	struct brcmf_proto_bcdc_dcmd msg;
94 	unsigned char buf[BRCMF_DCMD_MAXLEN];
95 	struct brcmf_fws_info *fws;
96 };
97 
98 
99 struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr)
100 {
101 	struct brcmf_bcdc *bcdc = drvr->proto->pd;
102 
103 	return bcdc->fws;
104 }
105 
106 static int
107 brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
108 		     uint len, bool set)
109 {
110 	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
111 	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
112 	u32 flags;
113 
114 	brcmf_dbg(BCDC, "Enter\n");
115 
116 	memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
117 
118 	msg->cmd = cpu_to_le32(cmd);
119 	msg->len = cpu_to_le32(len);
120 	flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);
121 	if (set)
122 		flags |= BCDC_DCMD_SET;
123 	flags = (flags & ~BCDC_DCMD_IF_MASK) |
124 		(ifidx << BCDC_DCMD_IF_SHIFT);
125 	msg->flags = cpu_to_le32(flags);
126 
127 	if (buf)
128 		memcpy(bcdc->buf, buf, len);
129 
130 	len += sizeof(*msg);
131 	if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE)
132 		len = BRCMF_TX_IOCTL_MAX_MSG_SIZE;
133 
134 	/* Send request */
135 	return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len);
136 }
137 
138 static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
139 {
140 	int ret;
141 	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
142 
143 	brcmf_dbg(BCDC, "Enter\n");
144 	len += sizeof(struct brcmf_proto_bcdc_dcmd);
145 	do {
146 		ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg,
147 				      len);
148 		if (ret < 0)
149 			break;
150 	} while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id);
151 
152 	return ret;
153 }
154 
155 static int
156 brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
157 			    void *buf, uint len, int *fwerr)
158 {
159 	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
160 	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
161 	void *info;
162 	int ret = 0, retries = 0;
163 	u32 id, flags;
164 
165 	brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
166 
167 	*fwerr = 0;
168 	ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);
169 	if (ret < 0) {
170 		bphy_err(drvr, "brcmf_proto_bcdc_msg failed w/status %d\n",
171 			 ret);
172 		goto done;
173 	}
174 
175 retry:
176 	/* wait for interrupt and get first fragment */
177 	ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
178 	if (ret < 0)
179 		goto done;
180 
181 	flags = le32_to_cpu(msg->flags);
182 	id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
183 
184 	if ((id < bcdc->reqid) && (++retries < RETRIES))
185 		goto retry;
186 	if (id != bcdc->reqid) {
187 		bphy_err(drvr, "%s: unexpected request id %d (expected %d)\n",
188 			 brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
189 			 bcdc->reqid);
190 		ret = -EINVAL;
191 		goto done;
192 	}
193 
194 	/* Check info buffer */
195 	info = (void *)&bcdc->buf[0];
196 
197 	/* Copy info buffer */
198 	if (buf) {
199 		if (ret < (int)len)
200 			len = ret;
201 		memcpy(buf, info, len);
202 	}
203 
204 	ret = 0;
205 
206 	/* Check the ERROR flag */
207 	if (flags & BCDC_DCMD_ERROR)
208 		*fwerr = le32_to_cpu(msg->status);
209 done:
210 	return ret;
211 }
212 
213 static int
214 brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
215 			  void *buf, uint len, int *fwerr)
216 {
217 	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
218 	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
219 	int ret;
220 	u32 flags, id;
221 
222 	brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
223 
224 	*fwerr = 0;
225 	ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);
226 	if (ret < 0)
227 		goto done;
228 
229 	ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
230 	if (ret < 0)
231 		goto done;
232 
233 	flags = le32_to_cpu(msg->flags);
234 	id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
235 
236 	if (id != bcdc->reqid) {
237 		bphy_err(drvr, "%s: unexpected request id %d (expected %d)\n",
238 			 brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
239 			 bcdc->reqid);
240 		ret = -EINVAL;
241 		goto done;
242 	}
243 
244 	ret = 0;
245 
246 	/* Check the ERROR flag */
247 	if (flags & BCDC_DCMD_ERROR)
248 		*fwerr = le32_to_cpu(msg->status);
249 
250 done:
251 	return ret;
252 }
253 
254 static void
255 brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
256 			 struct sk_buff *pktbuf)
257 {
258 	struct brcmf_proto_bcdc_header *h;
259 
260 	brcmf_dbg(BCDC, "Enter\n");
261 
262 	/* Push BDC header used to convey priority for buses that don't */
263 	skb_push(pktbuf, BCDC_HEADER_LEN);
264 
265 	h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
266 
267 	h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT);
268 	if (pktbuf->ip_summed == CHECKSUM_PARTIAL)
269 		h->flags |= BCDC_FLAG_SUM_NEEDED;
270 
271 	h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK);
272 	h->flags2 = 0;
273 	h->data_offset = offset;
274 	BCDC_SET_IF_IDX(h, ifidx);
275 	trace_brcmf_bcdchdr(pktbuf->data);
276 }
277 
278 static int
279 brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
280 			 struct sk_buff *pktbuf, struct brcmf_if **ifp)
281 {
282 	struct brcmf_proto_bcdc_header *h;
283 	struct brcmf_if *tmp_if;
284 
285 	brcmf_dbg(BCDC, "Enter\n");
286 
287 	/* Pop BCDC header used to convey priority for buses that don't */
288 	if (pktbuf->len <= BCDC_HEADER_LEN) {
289 		brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",
290 			  pktbuf->len, BCDC_HEADER_LEN);
291 		return -EBADE;
292 	}
293 
294 	trace_brcmf_bcdchdr(pktbuf->data);
295 	h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
296 
297 	tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
298 	if (!tmp_if) {
299 		brcmf_dbg(INFO, "no matching ifp found\n");
300 		return -EBADE;
301 	}
302 	if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
303 	    BCDC_PROTO_VER) {
304 		bphy_err(drvr, "%s: non-BCDC packet received, flags 0x%x\n",
305 			 brcmf_ifname(tmp_if), h->flags);
306 		return -EBADE;
307 	}
308 
309 	if (h->flags & BCDC_FLAG_SUM_GOOD) {
310 		brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
311 			  brcmf_ifname(tmp_if), h->flags);
312 		pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
313 	}
314 
315 	pktbuf->priority = h->priority & BCDC_PRIORITY_MASK;
316 
317 	skb_pull(pktbuf, BCDC_HEADER_LEN);
318 	if (do_fws)
319 		brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
320 	else
321 		skb_pull(pktbuf, h->data_offset << 2);
322 
323 	if (pktbuf->len == 0)
324 		return -ENODATA;
325 
326 	if (ifp != NULL)
327 		*ifp = tmp_if;
328 	return 0;
329 }
330 
331 static int brcmf_proto_bcdc_tx_queue_data(struct brcmf_pub *drvr, int ifidx,
332 					  struct sk_buff *skb)
333 {
334 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, ifidx);
335 	struct brcmf_bcdc *bcdc = drvr->proto->pd;
336 
337 	if (!brcmf_fws_queue_skbs(bcdc->fws))
338 		return brcmf_proto_txdata(drvr, ifidx, 0, skb);
339 
340 	return brcmf_fws_process_skb(ifp, skb);
341 }
342 
343 static int
344 brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset,
345 			struct sk_buff *pktbuf)
346 {
347 	brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf);
348 	return brcmf_bus_txdata(drvr->bus_if, pktbuf);
349 }
350 
351 void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state)
352 {
353 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
354 	struct brcmf_pub *drvr = bus_if->drvr;
355 
356 	brcmf_dbg(TRACE, "Enter\n");
357 
358 	brcmf_fws_bus_blocked(drvr, state);
359 }
360 
361 void
362 brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp,
363 			    bool success)
364 {
365 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
366 	struct brcmf_bcdc *bcdc = bus_if->drvr->proto->pd;
367 	struct brcmf_if *ifp;
368 
369 	/* await txstatus signal for firmware if active */
370 	if (brcmf_fws_fc_active(bcdc->fws)) {
371 		if (!success)
372 			brcmf_fws_bustxfail(bcdc->fws, txp);
373 	} else {
374 		if (brcmf_proto_bcdc_hdrpull(bus_if->drvr, false, txp, &ifp))
375 			brcmu_pkt_buf_free_skb(txp);
376 		else
377 			brcmf_txfinalize(ifp, txp, success);
378 	}
379 }
380 
381 static void
382 brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
383 				     enum proto_addr_mode addr_mode)
384 {
385 }
386 
387 static void
388 brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx,
389 			     u8 peer[ETH_ALEN])
390 {
391 }
392 
393 static void
394 brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx,
395 			       u8 peer[ETH_ALEN])
396 {
397 }
398 
399 static void brcmf_proto_bcdc_rxreorder(struct brcmf_if *ifp,
400 				       struct sk_buff *skb, bool inirq)
401 {
402 	brcmf_fws_rxreorder(ifp, skb, inirq);
403 }
404 
405 static void
406 brcmf_proto_bcdc_add_if(struct brcmf_if *ifp)
407 {
408 	brcmf_fws_add_interface(ifp);
409 }
410 
411 static void
412 brcmf_proto_bcdc_del_if(struct brcmf_if *ifp)
413 {
414 	brcmf_fws_del_interface(ifp);
415 }
416 
417 static void
418 brcmf_proto_bcdc_reset_if(struct brcmf_if *ifp)
419 {
420 	brcmf_fws_reset_interface(ifp);
421 }
422 
423 static int
424 brcmf_proto_bcdc_init_done(struct brcmf_pub *drvr)
425 {
426 	struct brcmf_bcdc *bcdc = drvr->proto->pd;
427 	struct brcmf_fws_info *fws;
428 
429 	fws = brcmf_fws_attach(drvr);
430 	if (IS_ERR(fws))
431 		return PTR_ERR(fws);
432 
433 	bcdc->fws = fws;
434 	return 0;
435 }
436 
437 static void brcmf_proto_bcdc_debugfs_create(struct brcmf_pub *drvr)
438 {
439 	brcmf_fws_debugfs_create(drvr);
440 }
441 
442 int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
443 {
444 	struct brcmf_bcdc *bcdc;
445 
446 	bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC);
447 	if (!bcdc)
448 		goto fail;
449 
450 	/* ensure that the msg buf directly follows the cdc msg struct */
451 	if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) {
452 		bphy_err(drvr, "struct brcmf_proto_bcdc is not correctly defined\n");
453 		goto fail;
454 	}
455 
456 	drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull;
457 	drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd;
458 	drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd;
459 	drvr->proto->tx_queue_data = brcmf_proto_bcdc_tx_queue_data;
460 	drvr->proto->txdata = brcmf_proto_bcdc_txdata;
461 	drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
462 	drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
463 	drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
464 	drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder;
465 	drvr->proto->add_if = brcmf_proto_bcdc_add_if;
466 	drvr->proto->del_if = brcmf_proto_bcdc_del_if;
467 	drvr->proto->reset_if = brcmf_proto_bcdc_reset_if;
468 	drvr->proto->init_done = brcmf_proto_bcdc_init_done;
469 	drvr->proto->debugfs_create = brcmf_proto_bcdc_debugfs_create;
470 	drvr->proto->pd = bcdc;
471 
472 	drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
473 	drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
474 			sizeof(struct brcmf_proto_bcdc_dcmd);
475 	return 0;
476 
477 fail:
478 	kfree(bcdc);
479 	return -ENOMEM;
480 }
481 
482 void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)
483 {
484 	struct brcmf_bcdc *bcdc = drvr->proto->pd;
485 
486 	drvr->proto->pd = NULL;
487 	brcmf_fws_detach(bcdc->fws);
488 	kfree(bcdc);
489 }
490