15b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
25b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Copyright (c) 2010 Broadcom Corporation
35b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
45b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Permission to use, copy, modify, and/or distribute this software for any
55b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * purpose with or without fee is hereby granted, provided that the above
65b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * copyright notice and this permission notice appear in all copies.
75b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
85b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
95b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*******************************************************************************
185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Communicates with the dongle by using dcmd codes.
195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * For certain dcmd codes, the dongle interprets string data from the host.
205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ******************************************************************************/
215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/types.h>
235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/netdevice.h>
245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <brcmu_utils.h>
265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <brcmu_wifi.h>
275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd.h"
295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_bus.h"
30349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel#include "fwsignal.h"
315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_dbg.h"
32ea0737d6e24b44b632e9094108bb987b0338ea74Arend van Spriel#include "tracepoint.h"
3385b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman#include "proto.h"
3485b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman#include "bcdc.h"
355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meulemanstruct brcmf_proto_bcdc_dcmd {
375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 cmd;	/* dongle command value */
385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 len;	/* lower 16: output buflen;
395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 * upper 16: input buflen (excludes header) */
405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 flags;	/* flag defns given below */
415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 status;	/* status code returned from the device */
425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
44aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman/* BCDC flag definitions */
45aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_DCMD_ERROR		0x01		/* 1=cmd failed */
46aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_DCMD_SET		0x02		/* 0=get, 1=set cmd */
47aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_DCMD_IF_MASK	0xF000		/* I/F index */
48aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_DCMD_IF_SHIFT	12
49aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_DCMD_ID_MASK	0xFFFF0000	/* id an cmd pairing */
50aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_DCMD_ID_SHIFT	16		/* ID Mask shift bits */
51aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_DCMD_ID(flags)	\
52aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	(((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT)
535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
55aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman * BCDC header - Broadcom specific extension of CDC.
565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Used on data packets to convey priority across USB.
575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
58aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define	BCDC_HEADER_LEN		4
59aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_PROTO_VER		2	/* Protocol version */
60aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_FLAG_VER_MASK	0xf0	/* Protocol version mask */
61aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_FLAG_VER_SHIFT	4	/* Protocol version shift */
62aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_FLAG_SUM_GOOD	0x04	/* Good RX checksums */
63aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_FLAG_SUM_NEEDED	0x08	/* Dongle needs to do TX checksums */
64aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_PRIORITY_MASK	0x7
65aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_FLAG2_IF_MASK	0x0f	/* packet rx interface in APSTA */
66aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_FLAG2_IF_SHIFT	0
67aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman
68aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_GET_IF_IDX(hdr) \
69aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT))
70aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman#define BCDC_SET_IF_IDX(hdr, idx) \
71aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \
72aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	((idx) << BCDC_FLAG2_IF_SHIFT)))
735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
74bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel/**
75aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman * struct brcmf_proto_bcdc_header - BCDC header format
76bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel *
77bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @flags: flags contain protocol and checksum info.
78bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @priority: 802.1d priority and USB flow control info (bit 4:7).
79bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @flags2: additional flags containing dongle interface index.
80bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @data_offset: start of packet data. header is following by firmware signals.
81bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel */
82aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meulemanstruct brcmf_proto_bcdc_header {
835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 flags;
84bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel	u8 priority;
855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 flags2;
86e40aed0638ac84d63a2ff33502e215ac81010a89Franky Lin	u8 data_offset;
875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
89bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel/*
90bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * maximum length of firmware signal data between
91aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman * the BCDC header and packet data in the tx path.
92bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel */
93bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES	12
945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define RETRIES 2 /* # of retries to retrieve matching dcmd response */
966e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin#define BUS_HEADER_LEN	(16+64)		/* Must be atleast SDPCM_RESERVE
975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 * (amount of header tha might be added)
985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 * plus any space that might be needed
996e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin					 * for bus alignment padding.
1005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 */
10185b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanstruct brcmf_bcdc {
1025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 reqid;
1035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 bus_header[BUS_HEADER_LEN];
104aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	struct brcmf_proto_bcdc_dcmd msg;
1058141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	unsigned char buf[BRCMF_DCMD_MAXLEN];
1065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
1075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1088141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman
1098141083c48192b63a39b77adffd9a8e0dcfab034Hante Meulemanstatic int
1108141083c48192b63a39b77adffd9a8e0dcfab034Hante Meulemanbrcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
1118141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman		     uint len, bool set)
1125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
11385b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
1148141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
1158141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	u32 flags;
1165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
117aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	brcmf_dbg(BCDC, "Enter\n");
1185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1198141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
1208141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman
1218141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	msg->cmd = cpu_to_le32(cmd);
1228141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	msg->len = cpu_to_le32(len);
1238141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);
1248141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	if (set)
1258141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman		flags |= BCDC_DCMD_SET;
1268141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	flags = (flags & ~BCDC_DCMD_IF_MASK) |
1278141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman		(ifidx << BCDC_DCMD_IF_SHIFT);
1288141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	msg->flags = cpu_to_le32(flags);
1298141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman
1308141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	if (buf)
1318141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman		memcpy(bcdc->buf, buf, len);
1325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
133d453399229cefff985391f079066ac5a84955537Hante Meuleman	len += sizeof(*msg);
134d453399229cefff985391f079066ac5a84955537Hante Meuleman	if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE)
135d453399229cefff985391f079066ac5a84955537Hante Meuleman		len = BRCMF_TX_IOCTL_MAX_MSG_SIZE;
136d453399229cefff985391f079066ac5a84955537Hante Meuleman
1375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Send request */
138d453399229cefff985391f079066ac5a84955537Hante Meuleman	return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len);
1395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
1405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
141aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meulemanstatic int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
1425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
1435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret;
14485b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
1455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
146aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	brcmf_dbg(BCDC, "Enter\n");
147aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	len += sizeof(struct brcmf_proto_bcdc_dcmd);
1485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	do {
14985b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman		ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg,
150d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel				      len);
1515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret < 0)
1525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
153aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	} while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id);
1545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
1565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
1575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15885b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanstatic int
15985b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanbrcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
16085b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman			    void *buf, uint len)
1615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
16285b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
163aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
1645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	void *info;
1655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret = 0, retries = 0;
1665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 id, flags;
1675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
168aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
1695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1708141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);
1715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0) {
172aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman		brcmf_err("brcmf_proto_bcdc_msg failed w/status %d\n",
1735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  ret);
1745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
1755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
1765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1775b435de0d786869c95d1962121af0d7df2542009Arend van Sprielretry:
1785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* wait for interrupt and get first fragment */
179aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
1805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0)
1815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
1825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = le32_to_cpu(msg->flags);
184aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
1855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18685b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	if ((id < bcdc->reqid) && (++retries < RETRIES))
1875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto retry;
18885b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	if (id != bcdc->reqid) {
1895e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("%s: unexpected request id %d (expected %d)\n",
19085b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman			  brcmf_ifname(drvr, ifidx), id, bcdc->reqid);
1915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = -EINVAL;
1925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
1935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
1945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check info buffer */
1965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	info = (void *)&msg[1];
1975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Copy info buffer */
1995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (buf) {
2005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret < (int)len)
2015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			len = ret;
2025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memcpy(buf, info, len);
2035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
2045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check the ERROR flag */
206aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	if (flags & BCDC_DCMD_ERROR)
2075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = le32_to_cpu(msg->status);
2085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2095b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone:
2105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
2115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21385b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanstatic int
21485b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanbrcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
21585b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman			  void *buf, uint len)
2165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
21785b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
218aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
2195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret = 0;
2205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 flags, id;
2215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
222aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
2235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2248141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman	ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);
2255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0)
2265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
2275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
228aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
2295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0)
2305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
2315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = le32_to_cpu(msg->flags);
233aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
2345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
23585b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	if (id != bcdc->reqid) {
2365e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("%s: unexpected request id %d (expected %d)\n",
23785b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman			  brcmf_ifname(drvr, ifidx), id, bcdc->reqid);
2385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = -EINVAL;
2395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
2405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
2415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check the ERROR flag */
243aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	if (flags & BCDC_DCMD_ERROR)
2445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = le32_to_cpu(msg->status);
2455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2465b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone:
2475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
2485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25085b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanstatic void
25185b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanbrcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
2525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 struct sk_buff *pktbuf)
2535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
254aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	struct brcmf_proto_bcdc_header *h;
2555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
256aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	brcmf_dbg(BCDC, "Enter\n");
2575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Push BDC header used to convey priority for buses that don't */
259aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	skb_push(pktbuf, BCDC_HEADER_LEN);
2605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
261aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
2625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
263aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT);
264aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	if (pktbuf->ip_summed == CHECKSUM_PARTIAL)
265aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman		h->flags |= BCDC_FLAG_SUM_NEEDED;
2665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
267aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK);
2685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h->flags2 = 0;
2698f0c3b6d44e09f497f57ca2997d903c5602336a1Arend van Spriel	h->data_offset = offset;
270aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	BCDC_SET_IF_IDX(h, ifidx);
271aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	trace_brcmf_bcdchdr(pktbuf->data);
2725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27485b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanstatic int
27585b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanbrcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
27685b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman			 struct sk_buff *pktbuf)
2775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
278aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	struct brcmf_proto_bcdc_header *h;
2795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
280aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	brcmf_dbg(BCDC, "Enter\n");
2815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
282aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	/* Pop BCDC header used to convey priority for buses that don't */
283aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	if (pktbuf->len <= BCDC_HEADER_LEN) {
2845cd02c7747596078e6b01f07f978fa8a10f4e9daPiotr Haber		brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",
285aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman			  pktbuf->len, BCDC_HEADER_LEN);
2865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EBADE;
2875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
2885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
289aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	trace_brcmf_bcdchdr(pktbuf->data);
290aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
2915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
292aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	*ifidx = BCDC_GET_IF_IDX(h);
2935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (*ifidx >= BRCMF_MAX_IFS) {
2945e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("rx data ifnum out of range (%d)\n", *ifidx);
2955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EBADE;
2965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
2972880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	/* The ifidx is the idx to map to matching netdev/ifp. When receiving
2982880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * events this is easy because it contains the bssidx which maps
2992880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
3002880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * bssidx 1 is used for p2p0 and no data can be received or
3012880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
3022880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 */
3032880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	if (*ifidx)
3042880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman		(*ifidx)++;
3055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
306aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
307aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	    BCDC_PROTO_VER) {
308aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman		brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
3095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_ifname(drvr, *ifidx), h->flags);
3105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EBADE;
3115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
313aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	if (h->flags & BCDC_FLAG_SUM_GOOD) {
314aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman		brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
3155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_ifname(drvr, *ifidx), h->flags);
316aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman		pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
3175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
319aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	pktbuf->priority = h->priority & BCDC_PRIORITY_MASK;
3205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
321aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	skb_pull(pktbuf, BCDC_HEADER_LEN);
322349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel	if (do_fws)
323349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel		brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf);
324349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel	else
325349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel		skb_pull(pktbuf, h->data_offset << 2);
3265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
327a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Spriel	if (pktbuf->len == 0)
328a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Spriel		return -ENODATA;
3295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
3305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3327b8a466e7cd36fc2acdba5c22a594e75d9b9f61aHante Meulemanstatic int
3337b8a466e7cd36fc2acdba5c22a594e75d9b9f61aHante Meulemanbrcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset,
3347b8a466e7cd36fc2acdba5c22a594e75d9b9f61aHante Meuleman			struct sk_buff *pktbuf)
3357b8a466e7cd36fc2acdba5c22a594e75d9b9f61aHante Meuleman{
3367b8a466e7cd36fc2acdba5c22a594e75d9b9f61aHante Meuleman	brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf);
3377b8a466e7cd36fc2acdba5c22a594e75d9b9f61aHante Meuleman	return brcmf_bus_txdata(drvr->bus_if, pktbuf);
3387b8a466e7cd36fc2acdba5c22a594e75d9b9f61aHante Meuleman}
3397b8a466e7cd36fc2acdba5c22a594e75d9b9f61aHante Meuleman
3408851cce085dce026f14e9fc525acc71e4c9d305bHante Meulemanstatic void
3418851cce085dce026f14e9fc525acc71e4c9d305bHante Meulemanbrcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
3428851cce085dce026f14e9fc525acc71e4c9d305bHante Meuleman				     enum proto_addr_mode addr_mode)
3438851cce085dce026f14e9fc525acc71e4c9d305bHante Meuleman{
3448851cce085dce026f14e9fc525acc71e4c9d305bHante Meuleman}
3458851cce085dce026f14e9fc525acc71e4c9d305bHante Meuleman
3468851cce085dce026f14e9fc525acc71e4c9d305bHante Meulemanstatic void
3478851cce085dce026f14e9fc525acc71e4c9d305bHante Meulemanbrcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx,
3488851cce085dce026f14e9fc525acc71e4c9d305bHante Meuleman			     u8 peer[ETH_ALEN])
3498851cce085dce026f14e9fc525acc71e4c9d305bHante Meuleman{
3508851cce085dce026f14e9fc525acc71e4c9d305bHante Meuleman}
3517b8a466e7cd36fc2acdba5c22a594e75d9b9f61aHante Meuleman
35270b7d94bcc1266fb91686bcd539ef81dff40eb3aHante Meulemanstatic void
35370b7d94bcc1266fb91686bcd539ef81dff40eb3aHante Meulemanbrcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx,
35470b7d94bcc1266fb91686bcd539ef81dff40eb3aHante Meuleman			       u8 peer[ETH_ALEN])
35570b7d94bcc1266fb91686bcd539ef81dff40eb3aHante Meuleman{
35670b7d94bcc1266fb91686bcd539ef81dff40eb3aHante Meuleman}
35770b7d94bcc1266fb91686bcd539ef81dff40eb3aHante Meuleman
35885b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanint brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
3595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
36085b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	struct brcmf_bcdc *bcdc;
3615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36285b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC);
36385b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	if (!bcdc)
3645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
3655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* ensure that the msg buf directly follows the cdc msg struct */
36785b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) {
36885b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman		brcmf_err("struct brcmf_proto_bcdc is not correctly defined\n");
3695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
3705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37285b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull;
37385b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd;
37485b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd;
3757b8a466e7cd36fc2acdba5c22a594e75d9b9f61aHante Meuleman	drvr->proto->txdata = brcmf_proto_bcdc_txdata;
3768851cce085dce026f14e9fc525acc71e4c9d305bHante Meuleman	drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
3778851cce085dce026f14e9fc525acc71e4c9d305bHante Meuleman	drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
37870b7d94bcc1266fb91686bcd539ef81dff40eb3aHante Meuleman	drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
37985b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	drvr->proto->pd = bcdc;
38085b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman
381aec87ce2f38c54dc7ef9bff2038e3b102a4e6c0cHante Meuleman	drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
382b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin	drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
3838141083c48192b63a39b77adffd9a8e0dcfab034Hante Meuleman			sizeof(struct brcmf_proto_bcdc_dcmd);
3845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
3855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3865b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail:
38785b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	kfree(bcdc);
3885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return -ENOMEM;
3895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39185b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meulemanvoid brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)
3925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
39385b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	kfree(drvr->proto->pd);
39485b8413371225e9bcbbb30cf3caa5889fed5f6a4Hante Meuleman	drvr->proto->pd = NULL;
3955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
396