cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bcdc.c (12701B)


      1// SPDX-License-Identifier: ISC
      2/*
      3 * Copyright (c) 2010 Broadcom Corporation
      4 */
      5
      6/*******************************************************************************
      7 * Communicates with the dongle by using dcmd codes.
      8 * For certain dcmd codes, the dongle interprets string data from the host.
      9 ******************************************************************************/
     10
     11#include <linux/types.h>
     12#include <linux/netdevice.h>
     13
     14#include <brcmu_utils.h>
     15#include <brcmu_wifi.h>
     16
     17#include "core.h"
     18#include "bus.h"
     19#include "fwsignal.h"
     20#include "debug.h"
     21#include "tracepoint.h"
     22#include "proto.h"
     23#include "bcdc.h"
     24
     25struct brcmf_proto_bcdc_dcmd {
     26	__le32 cmd;	/* dongle command value */
     27	__le32 len;	/* lower 16: output buflen;
     28			 * upper 16: input buflen (excludes header) */
     29	__le32 flags;	/* flag defns given below */
     30	__le32 status;	/* status code returned from the device */
     31};
     32
     33/* BCDC flag definitions */
     34#define BCDC_DCMD_ERROR		0x01		/* 1=cmd failed */
     35#define BCDC_DCMD_SET		0x02		/* 0=get, 1=set cmd */
     36#define BCDC_DCMD_IF_MASK	0xF000		/* I/F index */
     37#define BCDC_DCMD_IF_SHIFT	12
     38#define BCDC_DCMD_ID_MASK	0xFFFF0000	/* id an cmd pairing */
     39#define BCDC_DCMD_ID_SHIFT	16		/* ID Mask shift bits */
     40#define BCDC_DCMD_ID(flags)	\
     41	(((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT)
     42
     43/*
     44 * BCDC header - Broadcom specific extension of CDC.
     45 * Used on data packets to convey priority across USB.
     46 */
     47#define	BCDC_HEADER_LEN		4
     48#define BCDC_PROTO_VER		2	/* Protocol version */
     49#define BCDC_FLAG_VER_MASK	0xf0	/* Protocol version mask */
     50#define BCDC_FLAG_VER_SHIFT	4	/* Protocol version shift */
     51#define BCDC_FLAG_SUM_GOOD	0x04	/* Good RX checksums */
     52#define BCDC_FLAG_SUM_NEEDED	0x08	/* Dongle needs to do TX checksums */
     53#define BCDC_PRIORITY_MASK	0x7
     54#define BCDC_FLAG2_IF_MASK	0x0f	/* packet rx interface in APSTA */
     55#define BCDC_FLAG2_IF_SHIFT	0
     56
     57#define BCDC_GET_IF_IDX(hdr) \
     58	((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT))
     59#define BCDC_SET_IF_IDX(hdr, idx) \
     60	((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \
     61	((idx) << BCDC_FLAG2_IF_SHIFT)))
     62
     63/**
     64 * struct brcmf_proto_bcdc_header - BCDC header format
     65 *
     66 * @flags: flags contain protocol and checksum info.
     67 * @priority: 802.1d priority and USB flow control info (bit 4:7).
     68 * @flags2: additional flags containing dongle interface index.
     69 * @data_offset: start of packet data. header is following by firmware signals.
     70 */
     71struct brcmf_proto_bcdc_header {
     72	u8 flags;
     73	u8 priority;
     74	u8 flags2;
     75	u8 data_offset;
     76};
     77
     78/*
     79 * maximum length of firmware signal data between
     80 * the BCDC header and packet data in the tx path.
     81 */
     82#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES	12
     83
     84#define RETRIES 2 /* # of retries to retrieve matching dcmd response */
     85#define BUS_HEADER_LEN	(16+64)		/* Must be atleast SDPCM_RESERVE
     86					 * (amount of header tha might be added)
     87					 * plus any space that might be needed
     88					 * for bus alignment padding.
     89					 */
     90struct brcmf_bcdc {
     91	u16 reqid;
     92	u8 bus_header[BUS_HEADER_LEN];
     93	struct brcmf_proto_bcdc_dcmd msg;
     94	unsigned char buf[BRCMF_DCMD_MAXLEN];
     95	struct brcmf_fws_info *fws;
     96};
     97
     98
     99struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr)
    100{
    101	struct brcmf_bcdc *bcdc = drvr->proto->pd;
    102
    103	return bcdc->fws;
    104}
    105
    106static int
    107brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
    108		     uint len, bool set)
    109{
    110	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
    111	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
    112	u32 flags;
    113
    114	brcmf_dbg(BCDC, "Enter\n");
    115
    116	memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
    117
    118	msg->cmd = cpu_to_le32(cmd);
    119	msg->len = cpu_to_le32(len);
    120	flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);
    121	if (set)
    122		flags |= BCDC_DCMD_SET;
    123	flags = (flags & ~BCDC_DCMD_IF_MASK) |
    124		(ifidx << BCDC_DCMD_IF_SHIFT);
    125	msg->flags = cpu_to_le32(flags);
    126
    127	if (buf)
    128		memcpy(bcdc->buf, buf, len);
    129
    130	len += sizeof(*msg);
    131	if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE)
    132		len = BRCMF_TX_IOCTL_MAX_MSG_SIZE;
    133
    134	/* Send request */
    135	return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len);
    136}
    137
    138static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
    139{
    140	int ret;
    141	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
    142
    143	brcmf_dbg(BCDC, "Enter\n");
    144	len += sizeof(struct brcmf_proto_bcdc_dcmd);
    145	do {
    146		ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg,
    147				      len);
    148		if (ret < 0)
    149			break;
    150	} while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id);
    151
    152	return ret;
    153}
    154
    155static int
    156brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
    157			    void *buf, uint len, int *fwerr)
    158{
    159	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
    160	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
    161	void *info;
    162	int ret = 0, retries = 0;
    163	u32 id, flags;
    164
    165	brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
    166
    167	*fwerr = 0;
    168	ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);
    169	if (ret < 0) {
    170		bphy_err(drvr, "brcmf_proto_bcdc_msg failed w/status %d\n",
    171			 ret);
    172		goto done;
    173	}
    174
    175retry:
    176	/* wait for interrupt and get first fragment */
    177	ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
    178	if (ret < 0)
    179		goto done;
    180
    181	flags = le32_to_cpu(msg->flags);
    182	id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
    183
    184	if ((id < bcdc->reqid) && (++retries < RETRIES))
    185		goto retry;
    186	if (id != bcdc->reqid) {
    187		bphy_err(drvr, "%s: unexpected request id %d (expected %d)\n",
    188			 brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
    189			 bcdc->reqid);
    190		ret = -EINVAL;
    191		goto done;
    192	}
    193
    194	/* Check info buffer */
    195	info = (void *)&bcdc->buf[0];
    196
    197	/* Copy info buffer */
    198	if (buf) {
    199		if (ret < (int)len)
    200			len = ret;
    201		memcpy(buf, info, len);
    202	}
    203
    204	ret = 0;
    205
    206	/* Check the ERROR flag */
    207	if (flags & BCDC_DCMD_ERROR)
    208		*fwerr = le32_to_cpu(msg->status);
    209done:
    210	return ret;
    211}
    212
    213static int
    214brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
    215			  void *buf, uint len, int *fwerr)
    216{
    217	struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
    218	struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
    219	int ret;
    220	u32 flags, id;
    221
    222	brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
    223
    224	*fwerr = 0;
    225	ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);
    226	if (ret < 0)
    227		goto done;
    228
    229	ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
    230	if (ret < 0)
    231		goto done;
    232
    233	flags = le32_to_cpu(msg->flags);
    234	id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
    235
    236	if (id != bcdc->reqid) {
    237		bphy_err(drvr, "%s: unexpected request id %d (expected %d)\n",
    238			 brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
    239			 bcdc->reqid);
    240		ret = -EINVAL;
    241		goto done;
    242	}
    243
    244	ret = 0;
    245
    246	/* Check the ERROR flag */
    247	if (flags & BCDC_DCMD_ERROR)
    248		*fwerr = le32_to_cpu(msg->status);
    249
    250done:
    251	return ret;
    252}
    253
    254static void
    255brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
    256			 struct sk_buff *pktbuf)
    257{
    258	struct brcmf_proto_bcdc_header *h;
    259
    260	brcmf_dbg(BCDC, "Enter\n");
    261
    262	/* Push BDC header used to convey priority for buses that don't */
    263	skb_push(pktbuf, BCDC_HEADER_LEN);
    264
    265	h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
    266
    267	h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT);
    268	if (pktbuf->ip_summed == CHECKSUM_PARTIAL)
    269		h->flags |= BCDC_FLAG_SUM_NEEDED;
    270
    271	h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK);
    272	h->flags2 = 0;
    273	h->data_offset = offset;
    274	BCDC_SET_IF_IDX(h, ifidx);
    275	trace_brcmf_bcdchdr(pktbuf->data);
    276}
    277
    278static int
    279brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
    280			 struct sk_buff *pktbuf, struct brcmf_if **ifp)
    281{
    282	struct brcmf_proto_bcdc_header *h;
    283	struct brcmf_if *tmp_if;
    284
    285	brcmf_dbg(BCDC, "Enter\n");
    286
    287	/* Pop BCDC header used to convey priority for buses that don't */
    288	if (pktbuf->len <= BCDC_HEADER_LEN) {
    289		brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",
    290			  pktbuf->len, BCDC_HEADER_LEN);
    291		return -EBADE;
    292	}
    293
    294	trace_brcmf_bcdchdr(pktbuf->data);
    295	h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
    296
    297	tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
    298	if (!tmp_if) {
    299		brcmf_dbg(INFO, "no matching ifp found\n");
    300		return -EBADE;
    301	}
    302	if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
    303	    BCDC_PROTO_VER) {
    304		bphy_err(drvr, "%s: non-BCDC packet received, flags 0x%x\n",
    305			 brcmf_ifname(tmp_if), h->flags);
    306		return -EBADE;
    307	}
    308
    309	if (h->flags & BCDC_FLAG_SUM_GOOD) {
    310		brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
    311			  brcmf_ifname(tmp_if), h->flags);
    312		pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
    313	}
    314
    315	pktbuf->priority = h->priority & BCDC_PRIORITY_MASK;
    316
    317	skb_pull(pktbuf, BCDC_HEADER_LEN);
    318	if (do_fws)
    319		brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
    320	else
    321		skb_pull(pktbuf, h->data_offset << 2);
    322
    323	if (pktbuf->len == 0)
    324		return -ENODATA;
    325
    326	if (ifp != NULL)
    327		*ifp = tmp_if;
    328	return 0;
    329}
    330
    331static int brcmf_proto_bcdc_tx_queue_data(struct brcmf_pub *drvr, int ifidx,
    332					  struct sk_buff *skb)
    333{
    334	struct brcmf_if *ifp = brcmf_get_ifp(drvr, ifidx);
    335	struct brcmf_bcdc *bcdc = drvr->proto->pd;
    336
    337	if (!brcmf_fws_queue_skbs(bcdc->fws))
    338		return brcmf_proto_txdata(drvr, ifidx, 0, skb);
    339
    340	return brcmf_fws_process_skb(ifp, skb);
    341}
    342
    343static int
    344brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset,
    345			struct sk_buff *pktbuf)
    346{
    347	brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf);
    348	return brcmf_bus_txdata(drvr->bus_if, pktbuf);
    349}
    350
    351void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state)
    352{
    353	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
    354	struct brcmf_pub *drvr = bus_if->drvr;
    355
    356	brcmf_dbg(TRACE, "Enter\n");
    357
    358	brcmf_fws_bus_blocked(drvr, state);
    359}
    360
    361void
    362brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp,
    363			    bool success)
    364{
    365	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
    366	struct brcmf_bcdc *bcdc = bus_if->drvr->proto->pd;
    367	struct brcmf_if *ifp;
    368
    369	/* await txstatus signal for firmware if active */
    370	if (brcmf_fws_fc_active(bcdc->fws)) {
    371		if (!success)
    372			brcmf_fws_bustxfail(bcdc->fws, txp);
    373	} else {
    374		if (brcmf_proto_bcdc_hdrpull(bus_if->drvr, false, txp, &ifp))
    375			brcmu_pkt_buf_free_skb(txp);
    376		else
    377			brcmf_txfinalize(ifp, txp, success);
    378	}
    379}
    380
    381static void
    382brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
    383				     enum proto_addr_mode addr_mode)
    384{
    385}
    386
    387static void
    388brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx,
    389			     u8 peer[ETH_ALEN])
    390{
    391}
    392
    393static void
    394brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx,
    395			       u8 peer[ETH_ALEN])
    396{
    397}
    398
    399static void brcmf_proto_bcdc_rxreorder(struct brcmf_if *ifp,
    400				       struct sk_buff *skb)
    401{
    402	brcmf_fws_rxreorder(ifp, skb);
    403}
    404
    405static void
    406brcmf_proto_bcdc_add_if(struct brcmf_if *ifp)
    407{
    408	brcmf_fws_add_interface(ifp);
    409}
    410
    411static void
    412brcmf_proto_bcdc_del_if(struct brcmf_if *ifp)
    413{
    414	brcmf_fws_del_interface(ifp);
    415}
    416
    417static void
    418brcmf_proto_bcdc_reset_if(struct brcmf_if *ifp)
    419{
    420	brcmf_fws_reset_interface(ifp);
    421}
    422
    423static int
    424brcmf_proto_bcdc_init_done(struct brcmf_pub *drvr)
    425{
    426	struct brcmf_bcdc *bcdc = drvr->proto->pd;
    427	struct brcmf_fws_info *fws;
    428
    429	fws = brcmf_fws_attach(drvr);
    430	if (IS_ERR(fws))
    431		return PTR_ERR(fws);
    432
    433	bcdc->fws = fws;
    434	return 0;
    435}
    436
    437static void brcmf_proto_bcdc_debugfs_create(struct brcmf_pub *drvr)
    438{
    439	brcmf_fws_debugfs_create(drvr);
    440}
    441
    442int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
    443{
    444	struct brcmf_bcdc *bcdc;
    445
    446	bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC);
    447	if (!bcdc)
    448		goto fail;
    449
    450	/* ensure that the msg buf directly follows the cdc msg struct */
    451	if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) {
    452		bphy_err(drvr, "struct brcmf_proto_bcdc is not correctly defined\n");
    453		goto fail;
    454	}
    455
    456	drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull;
    457	drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd;
    458	drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd;
    459	drvr->proto->tx_queue_data = brcmf_proto_bcdc_tx_queue_data;
    460	drvr->proto->txdata = brcmf_proto_bcdc_txdata;
    461	drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
    462	drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
    463	drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
    464	drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder;
    465	drvr->proto->add_if = brcmf_proto_bcdc_add_if;
    466	drvr->proto->del_if = brcmf_proto_bcdc_del_if;
    467	drvr->proto->reset_if = brcmf_proto_bcdc_reset_if;
    468	drvr->proto->init_done = brcmf_proto_bcdc_init_done;
    469	drvr->proto->debugfs_create = brcmf_proto_bcdc_debugfs_create;
    470	drvr->proto->pd = bcdc;
    471
    472	drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
    473	drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
    474			sizeof(struct brcmf_proto_bcdc_dcmd);
    475	return 0;
    476
    477fail:
    478	kfree(bcdc);
    479	return -ENOMEM;
    480}
    481
    482void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)
    483{
    484	struct brcmf_bcdc *bcdc = drvr->proto->pd;
    485
    486	drvr->proto->pd = NULL;
    487	brcmf_fws_detach(bcdc->fws);
    488	kfree(bcdc);
    489}