bcdc.c revision ea0737d6e24b44b632e9094108bb987b0338ea74
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_proto.h"
305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_bus.h"
31349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel#include "fwsignal.h"
325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_dbg.h"
33ea0737d6e24b44b632e9094108bb987b0338ea74Arend van Spriel#include "tracepoint.h"
345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
355b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto_cdc_dcmd {
365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 cmd;	/* dongle command value */
375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 len;	/* lower 16: output buflen;
385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 * upper 16: input buflen (excludes header) */
395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 flags;	/* flag defns given below */
405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 status;	/* status code returned from the device */
415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Max valid buffer size that can be sent to the dongle */
445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_MAX_MSG_SIZE	(ETH_FRAME_LEN+ETH_FCS_LEN)
455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* CDC flag definitions */
475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ERROR		0x01	/* 1=cmd failed */
485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_SET		0x02	/* 0=get, 1=set cmd */
495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_IF_MASK	0xF000		/* I/F index */
505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_IF_SHIFT	12
515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID_MASK	0xFFFF0000	/* id an cmd pairing */
525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID_SHIFT	16		/* ID Mask shift bits */
535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID(flags)	\
545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	(((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT)
555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * BDC header - Broadcom specific extension of CDC.
585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Used on data packets to convey priority across USB.
595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	BDC_HEADER_LEN		4
61e40aed0638ac84d63a2ff33502e215ac81010a89Franky Lin#define BDC_PROTO_VER		2	/* Protocol version */
625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_VER_MASK	0xf0	/* Protocol version mask */
635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_VER_SHIFT	4	/* Protocol version shift */
645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_SUM_GOOD	0x04	/* Good RX checksums */
655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_SUM_NEEDED	0x08	/* Dongle needs to do TX checksums */
665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_PRIORITY_MASK	0x7
675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG2_IF_MASK	0x0f	/* packet rx interface in APSTA */
685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG2_IF_SHIFT	0
695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_GET_IF_IDX(hdr) \
715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT))
725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_SET_IF_IDX(hdr, idx) \
735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \
745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	((idx) << BDC_FLAG2_IF_SHIFT)))
755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
76bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel/**
77bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * struct brcmf_proto_bdc_header - BDC header format
78bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel *
79bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @flags: flags contain protocol and checksum info.
80bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @priority: 802.1d priority and USB flow control info (bit 4:7).
81bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @flags2: additional flags containing dongle interface index.
82bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @data_offset: start of packet data. header is following by firmware signals.
83bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel */
845b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto_bdc_header {
855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 flags;
86bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel	u8 priority;
875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 flags2;
88e40aed0638ac84d63a2ff33502e215ac81010a89Franky Lin	u8 data_offset;
895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
91bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel/*
92bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * maximum length of firmware signal data between
93bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * the BDC header and packet data in the tx path.
94bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel */
95bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES	12
965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define RETRIES 2 /* # of retries to retrieve matching dcmd response */
986e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin#define BUS_HEADER_LEN	(16+64)		/* Must be atleast SDPCM_RESERVE
995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 * (amount of header tha might be added)
1005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 * plus any space that might be needed
1016e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin					 * for bus alignment padding.
1025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 */
1036e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin#define ROUND_UP_MARGIN	2048	/* Biggest bus block size possible for
1045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 * round off at the end of buffer
1056e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin				 * Currently is SDIO
1065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 */
1075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1085b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto {
1095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 reqid;
1105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 bus_header[BUS_HEADER_LEN];
1115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto_cdc_dcmd msg;
1125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN];
1135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
1145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1155b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcmf_proto_cdc_msg(struct brcmf_pub *drvr)
1165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
1175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto *prot = drvr->prot;
1185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int len = le32_to_cpu(prot->msg.len) +
1195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			sizeof(struct brcmf_proto_cdc_dcmd);
1205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
121a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter\n");
1225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* NOTE : cdc->msg.len holds the desired length of the buffer to be
1245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
1255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 *        is actually sent to the dongle
1265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
1275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (len > CDC_MAX_MSG_SIZE)
1285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		len = CDC_MAX_MSG_SIZE;
1295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Send request */
131d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel	return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&prot->msg, len);
1325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
1335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1345b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
1355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
1365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret;
1375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto *prot = drvr->prot;
1385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
139a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter\n");
140d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel	len += sizeof(struct brcmf_proto_cdc_dcmd);
1415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	do {
142d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel		ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&prot->msg,
143d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel				      len);
1445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret < 0)
1455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
1465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} while (CDC_DCMD_ID(le32_to_cpu(prot->msg.flags)) != id);
1475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
1495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
1505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1515b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint
1525b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
1535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       void *buf, uint len)
1545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
1555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto *prot = drvr->prot;
1565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
1575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	void *info;
1585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret = 0, retries = 0;
1595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 id, flags;
1605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
161a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len);
1625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
1645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->cmd = cpu_to_le32(cmd);
1665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->len = cpu_to_le32(len);
1675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = (++prot->reqid << CDC_DCMD_ID_SHIFT);
1685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = (flags & ~CDC_DCMD_IF_MASK) |
1695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		(ifidx << CDC_DCMD_IF_SHIFT);
1705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->flags = cpu_to_le32(flags);
1715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (buf)
1735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memcpy(prot->buf, buf, len);
1745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret = brcmf_proto_cdc_msg(drvr);
1765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0) {
1775e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("brcmf_proto_cdc_msg failed w/status %d\n",
1785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  ret);
1795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
1805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
1815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1825b435de0d786869c95d1962121af0d7df2542009Arend van Sprielretry:
1835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* wait for interrupt and get first fragment */
1845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
1855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0)
1865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
1875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = le32_to_cpu(msg->flags);
1895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT;
1905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((id < prot->reqid) && (++retries < RETRIES))
1925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto retry;
1935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (id != prot->reqid) {
1945e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("%s: unexpected request id %d (expected %d)\n",
1955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_ifname(drvr, ifidx), id, prot->reqid);
1965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = -EINVAL;
1975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
1985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
1995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check info buffer */
2015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	info = (void *)&msg[1];
2025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Copy info buffer */
2045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (buf) {
2055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret < (int)len)
2065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			len = ret;
2075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memcpy(buf, info, len);
2085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
2095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check the ERROR flag */
21103abad08bb837650feb2bd7581ef14c8a1c51964Hante Meuleman	if (flags & CDC_DCMD_ERROR)
2125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = le32_to_cpu(msg->status);
2135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2145b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone:
2155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
2165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2185b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
2195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 void *buf, uint len)
2205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto *prot = drvr->prot;
2225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
2235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret = 0;
2245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 flags, id;
2255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
226a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len);
2275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
2295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->cmd = cpu_to_le32(cmd);
2315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->len = cpu_to_le32(len);
2325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET;
2335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = (flags & ~CDC_DCMD_IF_MASK) |
2345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		(ifidx << CDC_DCMD_IF_SHIFT);
2355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->flags = cpu_to_le32(flags);
2365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (buf)
2385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memcpy(prot->buf, buf, len);
2395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret = brcmf_proto_cdc_msg(drvr);
2415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0)
2425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
2435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
2455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0)
2465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
2475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = le32_to_cpu(msg->flags);
2495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT;
2505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (id != prot->reqid) {
2525e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("%s: unexpected request id %d (expected %d)\n",
2535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_ifname(drvr, ifidx), id, prot->reqid);
2545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = -EINVAL;
2555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
2565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
2575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check the ERROR flag */
25903abad08bb837650feb2bd7581ef14c8a1c51964Hante Meuleman	if (flags & CDC_DCMD_ERROR)
2605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = le32_to_cpu(msg->status);
2615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2625b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone:
2635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
2645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2665b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic bool pkt_sum_needed(struct sk_buff *skb)
2675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return skb->ip_summed == CHECKSUM_PARTIAL;
2695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2715b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void pkt_set_sum_good(struct sk_buff *skb, bool x)
2725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE);
2745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2768f0c3b6d44e09f497f57ca2997d903c5602336a1Arend van Sprielvoid brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
2775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 struct sk_buff *pktbuf)
2785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto_bdc_header *h;
2805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
281a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter\n");
2825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Push BDC header used to convey priority for buses that don't */
2845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	skb_push(pktbuf, BDC_HEADER_LEN);
2855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
2875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
2895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (pkt_sum_needed(pktbuf))
2905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		h->flags |= BDC_FLAG_SUM_NEEDED;
2915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h->priority = (pktbuf->priority & BDC_PRIORITY_MASK);
2935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h->flags2 = 0;
2948f0c3b6d44e09f497f57ca2997d903c5602336a1Arend van Spriel	h->data_offset = offset;
2955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	BDC_SET_IF_IDX(h, ifidx);
296ea0737d6e24b44b632e9094108bb987b0338ea74Arend van Spriel	trace_brcmf_bdchdr(pktbuf->data);
2975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
299349e7104ff662eeacca1fffbb480c287562c45a1Arend van Sprielint brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
3005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			struct sk_buff *pktbuf)
3015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto_bdc_header *h;
3035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
304a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter\n");
3055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Pop BDC header used to convey priority for buses that don't */
3075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3085cd02c7747596078e6b01f07f978fa8a10f4e9daPiotr Haber	if (pktbuf->len <= BDC_HEADER_LEN) {
3095cd02c7747596078e6b01f07f978fa8a10f4e9daPiotr Haber		brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",
3105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  pktbuf->len, BDC_HEADER_LEN);
3115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EBADE;
3125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
314ea0737d6e24b44b632e9094108bb987b0338ea74Arend van Spriel	trace_brcmf_bdchdr(pktbuf->data);
3155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
3165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*ifidx = BDC_GET_IF_IDX(h);
3185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (*ifidx >= BRCMF_MAX_IFS) {
3195e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("rx data ifnum out of range (%d)\n", *ifidx);
3205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EBADE;
3215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3222880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	/* The ifidx is the idx to map to matching netdev/ifp. When receiving
3232880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * events this is easy because it contains the bssidx which maps
3242880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
3252880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * bssidx 1 is used for p2p0 and no data can be received or
3262880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
3272880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 */
3282880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	if (*ifidx)
3292880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman		(*ifidx)++;
3305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) !=
3325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    BDC_PROTO_VER) {
3335e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("%s: non-BDC packet received, flags 0x%x\n",
3345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_ifname(drvr, *ifidx), h->flags);
3355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EBADE;
3365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (h->flags & BDC_FLAG_SUM_GOOD) {
339a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman		brcmf_dbg(CDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
3405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_ifname(drvr, *ifidx), h->flags);
3415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		pkt_set_sum_good(pktbuf, true);
3425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	pktbuf->priority = h->priority & BDC_PRIORITY_MASK;
3455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	skb_pull(pktbuf, BDC_HEADER_LEN);
347349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel	if (do_fws)
348349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel		brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf);
349349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel	else
350349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel		skb_pull(pktbuf, h->data_offset << 2);
3515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
352a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Spriel	if (pktbuf->len == 0)
353a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Spriel		return -ENODATA;
3545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
3555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3575b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcmf_proto_attach(struct brcmf_pub *drvr)
3585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto *cdc;
3605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC);
3625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!cdc)
3635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
3645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* ensure that the msg buf directly follows the cdc msg struct */
3665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) {
3675e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("struct brcmf_proto is not correctly defined\n");
3685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
3695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	drvr->prot = cdc;
372bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel	drvr->hdrlen += BDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
373b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin	drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
3745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN;
3755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
3765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3775b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail:
3785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	kfree(cdc);
3795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return -ENOMEM;
3805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
3835b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_proto_detach(struct brcmf_pub *drvr)
3845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	kfree(drvr->prot);
3865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	drvr->prot = NULL;
3875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3895b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_proto_stop(struct brcmf_pub *drvr)
3905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Nothing to do for CDC */
3925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
393