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