bcdc.c revision bee1b848877b9e4512bdda480f73cda12b593e2f
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"
315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_dbg.h"
325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
335b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto_cdc_dcmd {
345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 cmd;	/* dongle command value */
355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 len;	/* lower 16: output buflen;
365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 * upper 16: input buflen (excludes header) */
375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 flags;	/* flag defns given below */
385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 status;	/* status code returned from the device */
395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Max valid buffer size that can be sent to the dongle */
425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_MAX_MSG_SIZE	(ETH_FRAME_LEN+ETH_FCS_LEN)
435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* CDC flag definitions */
455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ERROR		0x01	/* 1=cmd failed */
465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_SET		0x02	/* 0=get, 1=set cmd */
475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_IF_MASK	0xF000		/* I/F index */
485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_IF_SHIFT	12
495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID_MASK	0xFFFF0000	/* id an cmd pairing */
505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID_SHIFT	16		/* ID Mask shift bits */
515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID(flags)	\
525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	(((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT)
535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * BDC header - Broadcom specific extension of CDC.
565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Used on data packets to convey priority across USB.
575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	BDC_HEADER_LEN		4
59e40aed0638ac84d63a2ff33502e215ac81010a89Franky Lin#define BDC_PROTO_VER		2	/* Protocol version */
605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_VER_MASK	0xf0	/* Protocol version mask */
615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_VER_SHIFT	4	/* Protocol version shift */
625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_SUM_GOOD	0x04	/* Good RX checksums */
635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_SUM_NEEDED	0x08	/* Dongle needs to do TX checksums */
645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_PRIORITY_MASK	0x7
655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG2_IF_MASK	0x0f	/* packet rx interface in APSTA */
665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG2_IF_SHIFT	0
675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_GET_IF_IDX(hdr) \
695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT))
705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_SET_IF_IDX(hdr, idx) \
715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \
725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	((idx) << BDC_FLAG2_IF_SHIFT)))
735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
74bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel/**
75bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * struct brcmf_proto_bdc_header - BDC 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 */
825b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto_bdc_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
91bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * the BDC 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					 */
1016e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin#define ROUND_UP_MARGIN	2048	/* Biggest bus block size possible for
1025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 * round off at the end of buffer
1036e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin				 * Currently is SDIO
1045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 */
1055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1065b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto {
1075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 reqid;
1085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 bus_header[BUS_HEADER_LEN];
1095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto_cdc_dcmd msg;
1105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN];
1115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
1125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1135b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcmf_proto_cdc_msg(struct brcmf_pub *drvr)
1145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
1155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto *prot = drvr->prot;
1165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int len = le32_to_cpu(prot->msg.len) +
1175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			sizeof(struct brcmf_proto_cdc_dcmd);
1185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
119a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter\n");
1205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* NOTE : cdc->msg.len holds the desired length of the buffer to be
1225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
1235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 *        is actually sent to the dongle
1245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
1255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (len > CDC_MAX_MSG_SIZE)
1265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		len = CDC_MAX_MSG_SIZE;
1275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Send request */
129d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel	return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&prot->msg, len);
1305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
1315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1325b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
1335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
1345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret;
1355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto *prot = drvr->prot;
1365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
137a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter\n");
138d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel	len += sizeof(struct brcmf_proto_cdc_dcmd);
1395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	do {
140d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel		ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&prot->msg,
141d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel				      len);
1425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret < 0)
1435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
1445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} while (CDC_DCMD_ID(le32_to_cpu(prot->msg.flags)) != id);
1455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
1475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
1485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1495b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint
1505b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
1515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       void *buf, uint len)
1525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
1535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto *prot = drvr->prot;
1545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
1555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	void *info;
1565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret = 0, retries = 0;
1575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 id, flags;
1585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
159a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len);
1605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
1625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->cmd = cpu_to_le32(cmd);
1645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->len = cpu_to_le32(len);
1655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = (++prot->reqid << CDC_DCMD_ID_SHIFT);
1665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = (flags & ~CDC_DCMD_IF_MASK) |
1675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		(ifidx << CDC_DCMD_IF_SHIFT);
1685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->flags = cpu_to_le32(flags);
1695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (buf)
1715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memcpy(prot->buf, buf, len);
1725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret = brcmf_proto_cdc_msg(drvr);
1745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0) {
1755e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("brcmf_proto_cdc_msg failed w/status %d\n",
1765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  ret);
1775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
1785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
1795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1805b435de0d786869c95d1962121af0d7df2542009Arend van Sprielretry:
1815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* wait for interrupt and get first fragment */
1825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
1835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0)
1845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
1855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = le32_to_cpu(msg->flags);
1875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT;
1885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((id < prot->reqid) && (++retries < RETRIES))
1905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto retry;
1915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (id != prot->reqid) {
1925e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("%s: unexpected request id %d (expected %d)\n",
1935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_ifname(drvr, ifidx), id, prot->reqid);
1945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = -EINVAL;
1955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
1965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
1975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check info buffer */
1995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	info = (void *)&msg[1];
2005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Copy info buffer */
2025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (buf) {
2035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret < (int)len)
2045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			len = ret;
2055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memcpy(buf, info, len);
2065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
2075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check the ERROR flag */
20903abad08bb837650feb2bd7581ef14c8a1c51964Hante Meuleman	if (flags & CDC_DCMD_ERROR)
2105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = le32_to_cpu(msg->status);
2115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2125b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone:
2135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
2145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2165b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
2175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 void *buf, uint len)
2185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto *prot = drvr->prot;
2205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
2215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret = 0;
2225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 flags, id;
2235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
224a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len);
2255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
2275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->cmd = cpu_to_le32(cmd);
2295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->len = cpu_to_le32(len);
2305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET;
2315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = (flags & ~CDC_DCMD_IF_MASK) |
2325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		(ifidx << CDC_DCMD_IF_SHIFT);
2335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msg->flags = cpu_to_le32(flags);
2345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (buf)
2365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memcpy(prot->buf, buf, len);
2375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret = brcmf_proto_cdc_msg(drvr);
2395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0)
2405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
2415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
2435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0)
2445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
2455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	flags = le32_to_cpu(msg->flags);
2475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT;
2485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (id != prot->reqid) {
2505e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("%s: unexpected request id %d (expected %d)\n",
2515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_ifname(drvr, ifidx), id, prot->reqid);
2525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = -EINVAL;
2535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
2545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
2555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check the ERROR flag */
25703abad08bb837650feb2bd7581ef14c8a1c51964Hante Meuleman	if (flags & CDC_DCMD_ERROR)
2585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = le32_to_cpu(msg->status);
2595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2605b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone:
2615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
2625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2645b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic bool pkt_sum_needed(struct sk_buff *skb)
2655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return skb->ip_summed == CHECKSUM_PARTIAL;
2675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2695b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void pkt_set_sum_good(struct sk_buff *skb, bool x)
2705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE);
2725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2745b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
2755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 struct sk_buff *pktbuf)
2765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto_bdc_header *h;
2785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
279a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter\n");
2805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Push BDC header used to convey priority for buses that don't */
2825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	skb_push(pktbuf, BDC_HEADER_LEN);
2845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
2865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
2885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (pkt_sum_needed(pktbuf))
2895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		h->flags |= BDC_FLAG_SUM_NEEDED;
2905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h->priority = (pktbuf->priority & BDC_PRIORITY_MASK);
2925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h->flags2 = 0;
293e40aed0638ac84d63a2ff33502e215ac81010a89Franky Lin	h->data_offset = 0;
2945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	BDC_SET_IF_IDX(h, ifidx);
2955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
297a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Sprielint brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
2985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			struct sk_buff *pktbuf)
2995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto_bdc_header *h;
3015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
302a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman	brcmf_dbg(CDC, "Enter\n");
3035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Pop BDC header used to convey priority for buses that don't */
3055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (pktbuf->len < BDC_HEADER_LEN) {
3075e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("rx data too short (%d < %d)\n",
3085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  pktbuf->len, BDC_HEADER_LEN);
3095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EBADE;
3105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
3135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*ifidx = BDC_GET_IF_IDX(h);
3155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (*ifidx >= BRCMF_MAX_IFS) {
3165e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("rx data ifnum out of range (%d)\n", *ifidx);
3175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EBADE;
3185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3192880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	/* The ifidx is the idx to map to matching netdev/ifp. When receiving
3202880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * events this is easy because it contains the bssidx which maps
3212880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
3222880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * bssidx 1 is used for p2p0 and no data can be received or
3232880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
3242880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	 */
3252880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman	if (*ifidx)
3262880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman		(*ifidx)++;
3275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) !=
3295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    BDC_PROTO_VER) {
3305e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("%s: non-BDC packet received, flags 0x%x\n",
3315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_ifname(drvr, *ifidx), h->flags);
3325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EBADE;
3335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (h->flags & BDC_FLAG_SUM_GOOD) {
336a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman		brcmf_dbg(CDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
3375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_ifname(drvr, *ifidx), h->flags);
3385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		pkt_set_sum_good(pktbuf, true);
3395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	pktbuf->priority = h->priority & BDC_PRIORITY_MASK;
3425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	skb_pull(pktbuf, BDC_HEADER_LEN);
3448f1ab44d8de5154cf6746d07f0902ea73f059c2bFranky Lin	skb_pull(pktbuf, h->data_offset << 2);
3455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
346a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Spriel	if (pktbuf->len == 0)
347a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Spriel		return -ENODATA;
3485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
3495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3515b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcmf_proto_attach(struct brcmf_pub *drvr)
3525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_proto *cdc;
3545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC);
3565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!cdc)
3575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
3585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* ensure that the msg buf directly follows the cdc msg struct */
3605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) {
3615e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel		brcmf_err("struct brcmf_proto is not correctly defined\n");
3625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
3635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	drvr->prot = cdc;
366bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel	drvr->hdrlen += BDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
367b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin	drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
3685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN;
3695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
3705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3715b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail:
3725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	kfree(cdc);
3735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return -ENOMEM;
3745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
3775b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_proto_detach(struct brcmf_pub *drvr)
3785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	kfree(drvr->prot);
3805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	drvr->prot = NULL;
3815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3835b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_proto_stop(struct brcmf_pub *drvr)
3845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Nothing to do for CDC */
3865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
387