xref: /openbmc/linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo  * Copyright (c) 2010 Broadcom Corporation
405491d2cSKalle Valo  */
505491d2cSKalle Valo 
605491d2cSKalle Valo /*******************************************************************************
705491d2cSKalle Valo  * Communicates with the dongle by using dcmd codes.
805491d2cSKalle Valo  * For certain dcmd codes, the dongle interprets string data from the host.
905491d2cSKalle Valo  ******************************************************************************/
1005491d2cSKalle Valo 
1105491d2cSKalle Valo #include <linux/types.h>
1205491d2cSKalle Valo #include <linux/netdevice.h>
1305491d2cSKalle Valo 
1405491d2cSKalle Valo #include <brcmu_utils.h>
1505491d2cSKalle Valo #include <brcmu_wifi.h>
1605491d2cSKalle Valo 
1705491d2cSKalle Valo #include "core.h"
1805491d2cSKalle Valo #include "bus.h"
1905491d2cSKalle Valo #include "fwsignal.h"
2005491d2cSKalle Valo #include "debug.h"
2105491d2cSKalle Valo #include "tracepoint.h"
2205491d2cSKalle Valo #include "proto.h"
2305491d2cSKalle Valo #include "bcdc.h"
2405491d2cSKalle Valo 
2505491d2cSKalle Valo struct brcmf_proto_bcdc_dcmd {
2605491d2cSKalle Valo 	__le32 cmd;	/* dongle command value */
2705491d2cSKalle Valo 	__le32 len;	/* lower 16: output buflen;
2805491d2cSKalle Valo 			 * upper 16: input buflen (excludes header) */
2905491d2cSKalle Valo 	__le32 flags;	/* flag defns given below */
3005491d2cSKalle Valo 	__le32 status;	/* status code returned from the device */
3105491d2cSKalle Valo };
3205491d2cSKalle Valo 
3305491d2cSKalle Valo /* BCDC flag definitions */
3405491d2cSKalle Valo #define BCDC_DCMD_ERROR		0x01		/* 1=cmd failed */
3505491d2cSKalle Valo #define BCDC_DCMD_SET		0x02		/* 0=get, 1=set cmd */
3605491d2cSKalle Valo #define BCDC_DCMD_IF_MASK	0xF000		/* I/F index */
3705491d2cSKalle Valo #define BCDC_DCMD_IF_SHIFT	12
3805491d2cSKalle Valo #define BCDC_DCMD_ID_MASK	0xFFFF0000	/* id an cmd pairing */
3905491d2cSKalle Valo #define BCDC_DCMD_ID_SHIFT	16		/* ID Mask shift bits */
4005491d2cSKalle Valo #define BCDC_DCMD_ID(flags)	\
4105491d2cSKalle Valo 	(((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT)
4205491d2cSKalle Valo 
4305491d2cSKalle Valo /*
4405491d2cSKalle Valo  * BCDC header - Broadcom specific extension of CDC.
4505491d2cSKalle Valo  * Used on data packets to convey priority across USB.
4605491d2cSKalle Valo  */
4705491d2cSKalle Valo #define	BCDC_HEADER_LEN		4
4805491d2cSKalle Valo #define BCDC_PROTO_VER		2	/* Protocol version */
4905491d2cSKalle Valo #define BCDC_FLAG_VER_MASK	0xf0	/* Protocol version mask */
5005491d2cSKalle Valo #define BCDC_FLAG_VER_SHIFT	4	/* Protocol version shift */
5105491d2cSKalle Valo #define BCDC_FLAG_SUM_GOOD	0x04	/* Good RX checksums */
5205491d2cSKalle Valo #define BCDC_FLAG_SUM_NEEDED	0x08	/* Dongle needs to do TX checksums */
5305491d2cSKalle Valo #define BCDC_PRIORITY_MASK	0x7
5405491d2cSKalle Valo #define BCDC_FLAG2_IF_MASK	0x0f	/* packet rx interface in APSTA */
5505491d2cSKalle Valo #define BCDC_FLAG2_IF_SHIFT	0
5605491d2cSKalle Valo 
5705491d2cSKalle Valo #define BCDC_GET_IF_IDX(hdr) \
5805491d2cSKalle Valo 	((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT))
5905491d2cSKalle Valo #define BCDC_SET_IF_IDX(hdr, idx) \
6005491d2cSKalle Valo 	((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \
6105491d2cSKalle Valo 	((idx) << BCDC_FLAG2_IF_SHIFT)))
6205491d2cSKalle Valo 
6305491d2cSKalle Valo /**
6405491d2cSKalle Valo  * struct brcmf_proto_bcdc_header - BCDC header format
6505491d2cSKalle Valo  *
6605491d2cSKalle Valo  * @flags: flags contain protocol and checksum info.
6705491d2cSKalle Valo  * @priority: 802.1d priority and USB flow control info (bit 4:7).
6805491d2cSKalle Valo  * @flags2: additional flags containing dongle interface index.
6905491d2cSKalle Valo  * @data_offset: start of packet data. header is following by firmware signals.
7005491d2cSKalle Valo  */
7105491d2cSKalle Valo struct brcmf_proto_bcdc_header {
7205491d2cSKalle Valo 	u8 flags;
7305491d2cSKalle Valo 	u8 priority;
7405491d2cSKalle Valo 	u8 flags2;
7505491d2cSKalle Valo 	u8 data_offset;
7605491d2cSKalle Valo };
7705491d2cSKalle Valo 
7805491d2cSKalle Valo /*
7905491d2cSKalle Valo  * maximum length of firmware signal data between
8005491d2cSKalle Valo  * the BCDC header and packet data in the tx path.
8105491d2cSKalle Valo  */
8205491d2cSKalle Valo #define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES	12
8305491d2cSKalle Valo 
8405491d2cSKalle Valo #define RETRIES 2 /* # of retries to retrieve matching dcmd response */
8505491d2cSKalle Valo #define BUS_HEADER_LEN	(16+64)		/* Must be atleast SDPCM_RESERVE
8605491d2cSKalle Valo 					 * (amount of header tha might be added)
8705491d2cSKalle Valo 					 * plus any space that might be needed
8805491d2cSKalle Valo 					 * for bus alignment padding.
8905491d2cSKalle Valo 					 */
90*11eda8f0SLo(Double)Hsiang Lo #define ROUND_UP_MARGIN 2048
91*11eda8f0SLo(Double)Hsiang Lo 
9205491d2cSKalle Valo struct brcmf_bcdc {
9305491d2cSKalle Valo 	u16 reqid;
9405491d2cSKalle Valo 	u8 bus_header[BUS_HEADER_LEN];
9505491d2cSKalle Valo 	struct brcmf_proto_bcdc_dcmd msg;
9605491d2cSKalle Valo 	unsigned char buf[BRCMF_DCMD_MAXLEN];
97acf8ac41SArend Van Spriel 	struct brcmf_fws_info *fws;
9805491d2cSKalle Valo };
9905491d2cSKalle Valo 
10005491d2cSKalle Valo 
drvr_to_fws(struct brcmf_pub * drvr)101acf8ac41SArend Van Spriel struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr)
102acf8ac41SArend Van Spriel {
103acf8ac41SArend Van Spriel 	struct brcmf_bcdc *bcdc = drvr->proto->pd;
104acf8ac41SArend Van Spriel 
105acf8ac41SArend Van Spriel 	return bcdc->fws;
106acf8ac41SArend Van Spriel }
107acf8ac41SArend Van Spriel 
10805491d2cSKalle Valo static int
brcmf_proto_bcdc_msg(struct brcmf_pub * drvr,int ifidx,uint cmd,void * buf,uint len,bool set)10905491d2cSKalle Valo brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
11005491d2cSKalle Valo 		     uint len, bool set)
11105491d2cSKalle Valo {
11205491d2cSKalle Valo 	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
11305491d2cSKalle Valo 	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
11405491d2cSKalle Valo 	u32 flags;
11505491d2cSKalle Valo 
11605491d2cSKalle Valo 	brcmf_dbg(BCDC, "Enter\n");
11705491d2cSKalle Valo 
11805491d2cSKalle Valo 	memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
11905491d2cSKalle Valo 
12005491d2cSKalle Valo 	msg->cmd = cpu_to_le32(cmd);
12105491d2cSKalle Valo 	msg->len = cpu_to_le32(len);
12205491d2cSKalle Valo 	flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);
12305491d2cSKalle Valo 	if (set)
12405491d2cSKalle Valo 		flags |= BCDC_DCMD_SET;
12505491d2cSKalle Valo 	flags = (flags & ~BCDC_DCMD_IF_MASK) |
12605491d2cSKalle Valo 		(ifidx << BCDC_DCMD_IF_SHIFT);
12705491d2cSKalle Valo 	msg->flags = cpu_to_le32(flags);
12805491d2cSKalle Valo 
12905491d2cSKalle Valo 	if (buf)
13005491d2cSKalle Valo 		memcpy(bcdc->buf, buf, len);
13105491d2cSKalle Valo 
13205491d2cSKalle Valo 	len += sizeof(*msg);
13305491d2cSKalle Valo 	if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE)
13405491d2cSKalle Valo 		len = BRCMF_TX_IOCTL_MAX_MSG_SIZE;
13505491d2cSKalle Valo 
13605491d2cSKalle Valo 	/* Send request */
13705491d2cSKalle Valo 	return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len);
13805491d2cSKalle Valo }
13905491d2cSKalle Valo 
brcmf_proto_bcdc_cmplt(struct brcmf_pub * drvr,u32 id,u32 len)14005491d2cSKalle Valo static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
14105491d2cSKalle Valo {
14205491d2cSKalle Valo 	int ret;
14305491d2cSKalle Valo 	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
14405491d2cSKalle Valo 
14505491d2cSKalle Valo 	brcmf_dbg(BCDC, "Enter\n");
14605491d2cSKalle Valo 	len += sizeof(struct brcmf_proto_bcdc_dcmd);
14705491d2cSKalle Valo 	do {
14805491d2cSKalle Valo 		ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg,
14905491d2cSKalle Valo 				      len);
15005491d2cSKalle Valo 		if (ret < 0)
15105491d2cSKalle Valo 			break;
15205491d2cSKalle Valo 	} while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id);
15305491d2cSKalle Valo 
15405491d2cSKalle Valo 	return ret;
15505491d2cSKalle Valo }
15605491d2cSKalle Valo 
15705491d2cSKalle Valo static int
brcmf_proto_bcdc_query_dcmd(struct brcmf_pub * drvr,int ifidx,uint cmd,void * buf,uint len,int * fwerr)15805491d2cSKalle Valo brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
159b69c1df4SArend Van Spriel 			    void *buf, uint len, int *fwerr)
16005491d2cSKalle Valo {
16105491d2cSKalle Valo 	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
16205491d2cSKalle Valo 	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
16305491d2cSKalle Valo 	void *info;
16405491d2cSKalle Valo 	int ret = 0, retries = 0;
16505491d2cSKalle Valo 	u32 id, flags;
16605491d2cSKalle Valo 
16705491d2cSKalle Valo 	brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
16805491d2cSKalle Valo 
169b69c1df4SArend Van Spriel 	*fwerr = 0;
17005491d2cSKalle Valo 	ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);
17105491d2cSKalle Valo 	if (ret < 0) {
172dcb1471bSRafał Miłecki 		bphy_err(drvr, "brcmf_proto_bcdc_msg failed w/status %d\n",
17305491d2cSKalle Valo 			 ret);
17405491d2cSKalle Valo 		goto done;
17505491d2cSKalle Valo 	}
17605491d2cSKalle Valo 
17705491d2cSKalle Valo retry:
17805491d2cSKalle Valo 	/* wait for interrupt and get first fragment */
17905491d2cSKalle Valo 	ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
18005491d2cSKalle Valo 	if (ret < 0)
18105491d2cSKalle Valo 		goto done;
18205491d2cSKalle Valo 
18305491d2cSKalle Valo 	flags = le32_to_cpu(msg->flags);
18405491d2cSKalle Valo 	id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
18505491d2cSKalle Valo 
18605491d2cSKalle Valo 	if ((id < bcdc->reqid) && (++retries < RETRIES))
18705491d2cSKalle Valo 		goto retry;
18805491d2cSKalle Valo 	if (id != bcdc->reqid) {
189dcb1471bSRafał Miłecki 		bphy_err(drvr, "%s: unexpected request id %d (expected %d)\n",
190c9c00438SHante Meuleman 			 brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
191c9c00438SHante Meuleman 			 bcdc->reqid);
19205491d2cSKalle Valo 		ret = -EINVAL;
19305491d2cSKalle Valo 		goto done;
19405491d2cSKalle Valo 	}
19505491d2cSKalle Valo 
19605491d2cSKalle Valo 	/* Check info buffer */
197704d1c6bSArend Van Spriel 	info = (void *)&bcdc->buf[0];
19805491d2cSKalle Valo 
19905491d2cSKalle Valo 	/* Copy info buffer */
20005491d2cSKalle Valo 	if (buf) {
20105491d2cSKalle Valo 		if (ret < (int)len)
20205491d2cSKalle Valo 			len = ret;
20305491d2cSKalle Valo 		memcpy(buf, info, len);
20405491d2cSKalle Valo 	}
20505491d2cSKalle Valo 
2065242a544SArend Van Spriel 	ret = 0;
2075242a544SArend Van Spriel 
20805491d2cSKalle Valo 	/* Check the ERROR flag */
20905491d2cSKalle Valo 	if (flags & BCDC_DCMD_ERROR)
210b69c1df4SArend Van Spriel 		*fwerr = le32_to_cpu(msg->status);
21105491d2cSKalle Valo done:
21205491d2cSKalle Valo 	return ret;
21305491d2cSKalle Valo }
21405491d2cSKalle Valo 
21505491d2cSKalle Valo static int
brcmf_proto_bcdc_set_dcmd(struct brcmf_pub * drvr,int ifidx,uint cmd,void * buf,uint len,int * fwerr)21605491d2cSKalle Valo brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
217b69c1df4SArend Van Spriel 			  void *buf, uint len, int *fwerr)
21805491d2cSKalle Valo {
21905491d2cSKalle Valo 	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
22005491d2cSKalle Valo 	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
2215242a544SArend Van Spriel 	int ret;
22205491d2cSKalle Valo 	u32 flags, id;
22305491d2cSKalle Valo 
22405491d2cSKalle Valo 	brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
22505491d2cSKalle Valo 
226b69c1df4SArend Van Spriel 	*fwerr = 0;
22705491d2cSKalle Valo 	ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);
22805491d2cSKalle Valo 	if (ret < 0)
22905491d2cSKalle Valo 		goto done;
23005491d2cSKalle Valo 
23105491d2cSKalle Valo 	ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
23205491d2cSKalle Valo 	if (ret < 0)
23305491d2cSKalle Valo 		goto done;
23405491d2cSKalle Valo 
23505491d2cSKalle Valo 	flags = le32_to_cpu(msg->flags);
23605491d2cSKalle Valo 	id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
23705491d2cSKalle Valo 
23805491d2cSKalle Valo 	if (id != bcdc->reqid) {
239dcb1471bSRafał Miłecki 		bphy_err(drvr, "%s: unexpected request id %d (expected %d)\n",
240c9c00438SHante Meuleman 			 brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
241c9c00438SHante Meuleman 			 bcdc->reqid);
24205491d2cSKalle Valo 		ret = -EINVAL;
24305491d2cSKalle Valo 		goto done;
24405491d2cSKalle Valo 	}
24505491d2cSKalle Valo 
2465242a544SArend Van Spriel 	ret = 0;
2475242a544SArend Van Spriel 
24805491d2cSKalle Valo 	/* Check the ERROR flag */
24905491d2cSKalle Valo 	if (flags & BCDC_DCMD_ERROR)
250b69c1df4SArend Van Spriel 		*fwerr = le32_to_cpu(msg->status);
25105491d2cSKalle Valo 
25205491d2cSKalle Valo done:
25305491d2cSKalle Valo 	return ret;
25405491d2cSKalle Valo }
25505491d2cSKalle Valo 
25605491d2cSKalle Valo static void
brcmf_proto_bcdc_hdrpush(struct brcmf_pub * drvr,int ifidx,u8 offset,struct sk_buff * pktbuf)25705491d2cSKalle Valo brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
25805491d2cSKalle Valo 			 struct sk_buff *pktbuf)
25905491d2cSKalle Valo {
26005491d2cSKalle Valo 	struct brcmf_proto_bcdc_header *h;
26105491d2cSKalle Valo 
26205491d2cSKalle Valo 	brcmf_dbg(BCDC, "Enter\n");
26305491d2cSKalle Valo 
26405491d2cSKalle Valo 	/* Push BDC header used to convey priority for buses that don't */
26505491d2cSKalle Valo 	skb_push(pktbuf, BCDC_HEADER_LEN);
26605491d2cSKalle Valo 
26705491d2cSKalle Valo 	h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
26805491d2cSKalle Valo 
26905491d2cSKalle Valo 	h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT);
27005491d2cSKalle Valo 	if (pktbuf->ip_summed == CHECKSUM_PARTIAL)
27105491d2cSKalle Valo 		h->flags |= BCDC_FLAG_SUM_NEEDED;
27205491d2cSKalle Valo 
27305491d2cSKalle Valo 	h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK);
27405491d2cSKalle Valo 	h->flags2 = 0;
27505491d2cSKalle Valo 	h->data_offset = offset;
27605491d2cSKalle Valo 	BCDC_SET_IF_IDX(h, ifidx);
27705491d2cSKalle Valo 	trace_brcmf_bcdchdr(pktbuf->data);
27805491d2cSKalle Valo }
27905491d2cSKalle Valo 
28005491d2cSKalle Valo static int
brcmf_proto_bcdc_hdrpull(struct brcmf_pub * drvr,bool do_fws,struct sk_buff * pktbuf,struct brcmf_if ** ifp)28105491d2cSKalle Valo brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
28205491d2cSKalle Valo 			 struct sk_buff *pktbuf, struct brcmf_if **ifp)
28305491d2cSKalle Valo {
28405491d2cSKalle Valo 	struct brcmf_proto_bcdc_header *h;
28505491d2cSKalle Valo 	struct brcmf_if *tmp_if;
28605491d2cSKalle Valo 
28705491d2cSKalle Valo 	brcmf_dbg(BCDC, "Enter\n");
28805491d2cSKalle Valo 
28905491d2cSKalle Valo 	/* Pop BCDC header used to convey priority for buses that don't */
29005491d2cSKalle Valo 	if (pktbuf->len <= BCDC_HEADER_LEN) {
29105491d2cSKalle Valo 		brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",
29205491d2cSKalle Valo 			  pktbuf->len, BCDC_HEADER_LEN);
29305491d2cSKalle Valo 		return -EBADE;
29405491d2cSKalle Valo 	}
29505491d2cSKalle Valo 
29605491d2cSKalle Valo 	trace_brcmf_bcdchdr(pktbuf->data);
29705491d2cSKalle Valo 	h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
29805491d2cSKalle Valo 
29905491d2cSKalle Valo 	tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
30005491d2cSKalle Valo 	if (!tmp_if) {
30105491d2cSKalle Valo 		brcmf_dbg(INFO, "no matching ifp found\n");
30205491d2cSKalle Valo 		return -EBADE;
30305491d2cSKalle Valo 	}
30405491d2cSKalle Valo 	if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
30505491d2cSKalle Valo 	    BCDC_PROTO_VER) {
306dcb1471bSRafał Miłecki 		bphy_err(drvr, "%s: non-BCDC packet received, flags 0x%x\n",
307c9c00438SHante Meuleman 			 brcmf_ifname(tmp_if), h->flags);
30805491d2cSKalle Valo 		return -EBADE;
30905491d2cSKalle Valo 	}
31005491d2cSKalle Valo 
31105491d2cSKalle Valo 	if (h->flags & BCDC_FLAG_SUM_GOOD) {
31205491d2cSKalle Valo 		brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
313c9c00438SHante Meuleman 			  brcmf_ifname(tmp_if), h->flags);
31405491d2cSKalle Valo 		pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
31505491d2cSKalle Valo 	}
31605491d2cSKalle Valo 
31705491d2cSKalle Valo 	pktbuf->priority = h->priority & BCDC_PRIORITY_MASK;
31805491d2cSKalle Valo 
31905491d2cSKalle Valo 	skb_pull(pktbuf, BCDC_HEADER_LEN);
32005491d2cSKalle Valo 	if (do_fws)
32105491d2cSKalle Valo 		brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
32205491d2cSKalle Valo 	else
32305491d2cSKalle Valo 		skb_pull(pktbuf, h->data_offset << 2);
32405491d2cSKalle Valo 
32505491d2cSKalle Valo 	if (pktbuf->len == 0)
32605491d2cSKalle Valo 		return -ENODATA;
32705491d2cSKalle Valo 
32853985dccSPer Forlin 	if (ifp != NULL)
32905491d2cSKalle Valo 		*ifp = tmp_if;
33005491d2cSKalle Valo 	return 0;
33105491d2cSKalle Valo }
33205491d2cSKalle Valo 
brcmf_proto_bcdc_tx_queue_data(struct brcmf_pub * drvr,int ifidx,struct sk_buff * skb)333b073ac1fSRafał Miłecki static int brcmf_proto_bcdc_tx_queue_data(struct brcmf_pub *drvr, int ifidx,
334b073ac1fSRafał Miłecki 					  struct sk_buff *skb)
335b073ac1fSRafał Miłecki {
336b073ac1fSRafał Miłecki 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, ifidx);
337acf8ac41SArend Van Spriel 	struct brcmf_bcdc *bcdc = drvr->proto->pd;
338b073ac1fSRafał Miłecki 
339acf8ac41SArend Van Spriel 	if (!brcmf_fws_queue_skbs(bcdc->fws))
340b073ac1fSRafał Miłecki 		return brcmf_proto_txdata(drvr, ifidx, 0, skb);
341b073ac1fSRafał Miłecki 
342b073ac1fSRafał Miłecki 	return brcmf_fws_process_skb(ifp, skb);
343b073ac1fSRafał Miłecki }
344b073ac1fSRafał Miłecki 
34505491d2cSKalle Valo static int
brcmf_proto_bcdc_txdata(struct brcmf_pub * drvr,int ifidx,u8 offset,struct sk_buff * pktbuf)34605491d2cSKalle Valo brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset,
34705491d2cSKalle Valo 			struct sk_buff *pktbuf)
34805491d2cSKalle Valo {
34905491d2cSKalle Valo 	brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf);
35005491d2cSKalle Valo 	return brcmf_bus_txdata(drvr->bus_if, pktbuf);
35105491d2cSKalle Valo }
35205491d2cSKalle Valo 
brcmf_proto_bcdc_txflowblock(struct device * dev,bool state)35320ec4f57SFranky Lin void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state)
35420ec4f57SFranky Lin {
35520ec4f57SFranky Lin 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
35620ec4f57SFranky Lin 	struct brcmf_pub *drvr = bus_if->drvr;
35720ec4f57SFranky Lin 
35820ec4f57SFranky Lin 	brcmf_dbg(TRACE, "Enter\n");
35920ec4f57SFranky Lin 
36020ec4f57SFranky Lin 	brcmf_fws_bus_blocked(drvr, state);
36120ec4f57SFranky Lin }
36220ec4f57SFranky Lin 
3637b584396SFranky Lin void
brcmf_proto_bcdc_txcomplete(struct device * dev,struct sk_buff * txp,bool success)3647b584396SFranky Lin brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp,
3657b584396SFranky Lin 			    bool success)
3667b584396SFranky Lin {
3677b584396SFranky Lin 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
368acf8ac41SArend Van Spriel 	struct brcmf_bcdc *bcdc = bus_if->drvr->proto->pd;
3697b584396SFranky Lin 	struct brcmf_if *ifp;
3707b584396SFranky Lin 
3717b584396SFranky Lin 	/* await txstatus signal for firmware if active */
372acf8ac41SArend Van Spriel 	if (brcmf_fws_fc_active(bcdc->fws)) {
3732eee3db7SWataru Gohda 		brcmf_fws_bustxcomplete(bcdc->fws, txp, success);
3747b584396SFranky Lin 	} else {
375acf8ac41SArend Van Spriel 		if (brcmf_proto_bcdc_hdrpull(bus_if->drvr, false, txp, &ifp))
3767b584396SFranky Lin 			brcmu_pkt_buf_free_skb(txp);
3777b584396SFranky Lin 		else
3787b584396SFranky Lin 			brcmf_txfinalize(ifp, txp, success);
3797b584396SFranky Lin 	}
3807b584396SFranky Lin }
3817b584396SFranky Lin 
38205491d2cSKalle Valo static void
brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub * drvr,int ifidx,enum proto_addr_mode addr_mode)38305491d2cSKalle Valo brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
38405491d2cSKalle Valo 				     enum proto_addr_mode addr_mode)
38505491d2cSKalle Valo {
38605491d2cSKalle Valo }
38705491d2cSKalle Valo 
38805491d2cSKalle Valo static void
brcmf_proto_bcdc_delete_peer(struct brcmf_pub * drvr,int ifidx,u8 peer[ETH_ALEN])38905491d2cSKalle Valo brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx,
39005491d2cSKalle Valo 			     u8 peer[ETH_ALEN])
39105491d2cSKalle Valo {
39205491d2cSKalle Valo }
39305491d2cSKalle Valo 
39405491d2cSKalle Valo static void
brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub * drvr,int ifidx,u8 peer[ETH_ALEN])39505491d2cSKalle Valo brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx,
39605491d2cSKalle Valo 			       u8 peer[ETH_ALEN])
39705491d2cSKalle Valo {
39805491d2cSKalle Valo }
39905491d2cSKalle Valo 
brcmf_proto_bcdc_rxreorder(struct brcmf_if * ifp,struct sk_buff * skb)400bbd1f932SArend van Spriel static void brcmf_proto_bcdc_rxreorder(struct brcmf_if *ifp,
401b381728eSSebastian Andrzej Siewior 				       struct sk_buff *skb)
402bbd1f932SArend van Spriel {
403b381728eSSebastian Andrzej Siewior 	brcmf_fws_rxreorder(ifp, skb);
404bbd1f932SArend van Spriel }
405bbd1f932SArend van Spriel 
4069fdc64bbSFranky Lin static void
brcmf_proto_bcdc_add_if(struct brcmf_if * ifp)4079fdc64bbSFranky Lin brcmf_proto_bcdc_add_if(struct brcmf_if *ifp)
4089fdc64bbSFranky Lin {
4099fdc64bbSFranky Lin 	brcmf_fws_add_interface(ifp);
4109fdc64bbSFranky Lin }
4119fdc64bbSFranky Lin 
412c02a5eb8SFranky Lin static void
brcmf_proto_bcdc_del_if(struct brcmf_if * ifp)413c02a5eb8SFranky Lin brcmf_proto_bcdc_del_if(struct brcmf_if *ifp)
414c02a5eb8SFranky Lin {
415c02a5eb8SFranky Lin 	brcmf_fws_del_interface(ifp);
416c02a5eb8SFranky Lin }
417c02a5eb8SFranky Lin 
41866ded1f8SFranky Lin static void
brcmf_proto_bcdc_reset_if(struct brcmf_if * ifp)41966ded1f8SFranky Lin brcmf_proto_bcdc_reset_if(struct brcmf_if *ifp)
42066ded1f8SFranky Lin {
42166ded1f8SFranky Lin 	brcmf_fws_reset_interface(ifp);
42266ded1f8SFranky Lin }
42366ded1f8SFranky Lin 
42462c50a34SFranky Lin static int
brcmf_proto_bcdc_init_done(struct brcmf_pub * drvr)42562c50a34SFranky Lin brcmf_proto_bcdc_init_done(struct brcmf_pub *drvr)
42662c50a34SFranky Lin {
427acf8ac41SArend Van Spriel 	struct brcmf_bcdc *bcdc = drvr->proto->pd;
428acf8ac41SArend Van Spriel 	struct brcmf_fws_info *fws;
429acf8ac41SArend Van Spriel 
430acf8ac41SArend Van Spriel 	fws = brcmf_fws_attach(drvr);
431acf8ac41SArend Van Spriel 	if (IS_ERR(fws))
432acf8ac41SArend Van Spriel 		return PTR_ERR(fws);
433acf8ac41SArend Van Spriel 
434acf8ac41SArend Van Spriel 	bcdc->fws = fws;
435acf8ac41SArend Van Spriel 	return 0;
43662c50a34SFranky Lin }
43762c50a34SFranky Lin 
brcmf_proto_bcdc_debugfs_create(struct brcmf_pub * drvr)43834789d0cSArend Van Spriel static void brcmf_proto_bcdc_debugfs_create(struct brcmf_pub *drvr)
43934789d0cSArend Van Spriel {
44034789d0cSArend Van Spriel 	brcmf_fws_debugfs_create(drvr);
44134789d0cSArend Van Spriel }
44234789d0cSArend Van Spriel 
brcmf_proto_bcdc_attach(struct brcmf_pub * drvr)44305491d2cSKalle Valo int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
44405491d2cSKalle Valo {
44505491d2cSKalle Valo 	struct brcmf_bcdc *bcdc;
44605491d2cSKalle Valo 
44705491d2cSKalle Valo 	bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC);
44805491d2cSKalle Valo 	if (!bcdc)
44905491d2cSKalle Valo 		goto fail;
45005491d2cSKalle Valo 
45105491d2cSKalle Valo 	/* ensure that the msg buf directly follows the cdc msg struct */
45205491d2cSKalle Valo 	if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) {
453dcb1471bSRafał Miłecki 		bphy_err(drvr, "struct brcmf_proto_bcdc is not correctly defined\n");
45405491d2cSKalle Valo 		goto fail;
45505491d2cSKalle Valo 	}
45605491d2cSKalle Valo 
45705491d2cSKalle Valo 	drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull;
45805491d2cSKalle Valo 	drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd;
45905491d2cSKalle Valo 	drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd;
460b073ac1fSRafał Miłecki 	drvr->proto->tx_queue_data = brcmf_proto_bcdc_tx_queue_data;
46105491d2cSKalle Valo 	drvr->proto->txdata = brcmf_proto_bcdc_txdata;
46205491d2cSKalle Valo 	drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
46305491d2cSKalle Valo 	drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
46405491d2cSKalle Valo 	drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
465bbd1f932SArend van Spriel 	drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder;
4669fdc64bbSFranky Lin 	drvr->proto->add_if = brcmf_proto_bcdc_add_if;
467c02a5eb8SFranky Lin 	drvr->proto->del_if = brcmf_proto_bcdc_del_if;
46866ded1f8SFranky Lin 	drvr->proto->reset_if = brcmf_proto_bcdc_reset_if;
46962c50a34SFranky Lin 	drvr->proto->init_done = brcmf_proto_bcdc_init_done;
47034789d0cSArend Van Spriel 	drvr->proto->debugfs_create = brcmf_proto_bcdc_debugfs_create;
47105491d2cSKalle Valo 	drvr->proto->pd = bcdc;
47205491d2cSKalle Valo 
47305491d2cSKalle Valo 	drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
47405491d2cSKalle Valo 	drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
475*11eda8f0SLo(Double)Hsiang Lo 			sizeof(struct brcmf_proto_bcdc_dcmd) + ROUND_UP_MARGIN;
47605491d2cSKalle Valo 	return 0;
47705491d2cSKalle Valo 
47805491d2cSKalle Valo fail:
47905491d2cSKalle Valo 	kfree(bcdc);
48005491d2cSKalle Valo 	return -ENOMEM;
48105491d2cSKalle Valo }
48205491d2cSKalle Valo 
brcmf_proto_bcdc_detach(struct brcmf_pub * drvr)483a84a60ccSArend van Spriel void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)
48405491d2cSKalle Valo {
485acf8ac41SArend Van Spriel 	struct brcmf_bcdc *bcdc = drvr->proto->pd;
486acf8ac41SArend Van Spriel 
48705491d2cSKalle Valo 	drvr->proto->pd = NULL;
488a84a60ccSArend van Spriel 	brcmf_fws_detach(bcdc->fws);
489acf8ac41SArend Van Spriel 	kfree(bcdc);
49005491d2cSKalle Valo }
491