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