491 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2010 Broadcom Corporation4 */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 1238#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 448#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 0x754#define BCDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */55#define BCDC_FLAG2_IF_SHIFT 056 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 format65 *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 between80 * the BCDC header and packet data in the tx path.81 */82#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 1283 84#define RETRIES 2 /* # of retries to retrieve matching dcmd response */85#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE86 * (amount of header tha might be added)87 * plus any space that might be needed88 * for bus alignment padding.89 */90#define ROUND_UP_MARGIN 204891 92struct brcmf_bcdc {93 u16 reqid;94 u8 bus_header[BUS_HEADER_LEN];95 struct brcmf_proto_bcdc_dcmd msg;96 unsigned char buf[BRCMF_DCMD_MAXLEN];97 struct brcmf_fws_info *fws;98};99 100 101struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr)102{103 struct brcmf_bcdc *bcdc = drvr->proto->pd;104 105 return bcdc->fws;106}107 108static int109brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,110 uint len, bool set)111{112 struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;113 struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;114 u32 flags;115 116 brcmf_dbg(BCDC, "Enter\n");117 118 memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));119 120 msg->cmd = cpu_to_le32(cmd);121 msg->len = cpu_to_le32(len);122 flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);123 if (set)124 flags |= BCDC_DCMD_SET;125 flags = (flags & ~BCDC_DCMD_IF_MASK) |126 (ifidx << BCDC_DCMD_IF_SHIFT);127 msg->flags = cpu_to_le32(flags);128 129 if (buf)130 memcpy(bcdc->buf, buf, len);131 132 len += sizeof(*msg);133 if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE)134 len = BRCMF_TX_IOCTL_MAX_MSG_SIZE;135 136 /* Send request */137 return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len);138}139 140static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)141{142 int ret;143 struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;144 145 brcmf_dbg(BCDC, "Enter\n");146 len += sizeof(struct brcmf_proto_bcdc_dcmd);147 do {148 ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg,149 len);150 if (ret < 0)151 break;152 } while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id);153 154 return ret;155}156 157static int158brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,159 void *buf, uint len, int *fwerr)160{161 struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;162 struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;163 void *info;164 int ret = 0, retries = 0;165 u32 id, flags;166 167 brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);168 169 *fwerr = 0;170 ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);171 if (ret < 0) {172 bphy_err(drvr, "brcmf_proto_bcdc_msg failed w/status %d\n",173 ret);174 goto done;175 }176 177retry:178 /* wait for interrupt and get first fragment */179 ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);180 if (ret < 0)181 goto done;182 183 flags = le32_to_cpu(msg->flags);184 id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;185 186 if ((id < bcdc->reqid) && (++retries < RETRIES))187 goto retry;188 if (id != bcdc->reqid) {189 bphy_err(drvr, "%s: unexpected request id %d (expected %d)\n",190 brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,191 bcdc->reqid);192 ret = -EINVAL;193 goto done;194 }195 196 /* Check info buffer */197 info = (void *)&bcdc->buf[0];198 199 /* Copy info buffer */200 if (buf) {201 if (ret < (int)len)202 len = ret;203 memcpy(buf, info, len);204 }205 206 ret = 0;207 208 /* Check the ERROR flag */209 if (flags & BCDC_DCMD_ERROR)210 *fwerr = le32_to_cpu(msg->status);211done:212 return ret;213}214 215static int216brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,217 void *buf, uint len, int *fwerr)218{219 struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;220 struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;221 int ret;222 u32 flags, id;223 224 brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);225 226 *fwerr = 0;227 ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);228 if (ret < 0)229 goto done;230 231 ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);232 if (ret < 0)233 goto done;234 235 flags = le32_to_cpu(msg->flags);236 id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;237 238 if (id != bcdc->reqid) {239 bphy_err(drvr, "%s: unexpected request id %d (expected %d)\n",240 brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,241 bcdc->reqid);242 ret = -EINVAL;243 goto done;244 }245 246 ret = 0;247 248 /* Check the ERROR flag */249 if (flags & BCDC_DCMD_ERROR)250 *fwerr = le32_to_cpu(msg->status);251 252done:253 return ret;254}255 256static void257brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,258 struct sk_buff *pktbuf)259{260 struct brcmf_proto_bcdc_header *h;261 262 brcmf_dbg(BCDC, "Enter\n");263 264 /* Push BDC header used to convey priority for buses that don't */265 skb_push(pktbuf, BCDC_HEADER_LEN);266 267 h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);268 269 h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT);270 if (pktbuf->ip_summed == CHECKSUM_PARTIAL)271 h->flags |= BCDC_FLAG_SUM_NEEDED;272 273 h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK);274 h->flags2 = 0;275 h->data_offset = offset;276 BCDC_SET_IF_IDX(h, ifidx);277 trace_brcmf_bcdchdr(pktbuf->data);278}279 280static int281brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,282 struct sk_buff *pktbuf, struct brcmf_if **ifp)283{284 struct brcmf_proto_bcdc_header *h;285 struct brcmf_if *tmp_if;286 287 brcmf_dbg(BCDC, "Enter\n");288 289 /* Pop BCDC header used to convey priority for buses that don't */290 if (pktbuf->len <= BCDC_HEADER_LEN) {291 brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",292 pktbuf->len, BCDC_HEADER_LEN);293 return -EBADE;294 }295 296 trace_brcmf_bcdchdr(pktbuf->data);297 h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);298 299 tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));300 if (!tmp_if) {301 brcmf_dbg(INFO, "no matching ifp found\n");302 return -EBADE;303 }304 if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=305 BCDC_PROTO_VER) {306 bphy_err(drvr, "%s: non-BCDC packet received, flags 0x%x\n",307 brcmf_ifname(tmp_if), h->flags);308 return -EBADE;309 }310 311 if (h->flags & BCDC_FLAG_SUM_GOOD) {312 brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",313 brcmf_ifname(tmp_if), h->flags);314 pktbuf->ip_summed = CHECKSUM_UNNECESSARY;315 }316 317 pktbuf->priority = h->priority & BCDC_PRIORITY_MASK;318 319 skb_pull(pktbuf, BCDC_HEADER_LEN);320 if (do_fws)321 brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);322 else323 skb_pull(pktbuf, h->data_offset << 2);324 325 if (pktbuf->len == 0)326 return -ENODATA;327 328 if (ifp != NULL)329 *ifp = tmp_if;330 return 0;331}332 333static int brcmf_proto_bcdc_tx_queue_data(struct brcmf_pub *drvr, int ifidx,334 struct sk_buff *skb)335{336 struct brcmf_if *ifp = brcmf_get_ifp(drvr, ifidx);337 struct brcmf_bcdc *bcdc = drvr->proto->pd;338 339 if (!brcmf_fws_queue_skbs(bcdc->fws))340 return brcmf_proto_txdata(drvr, ifidx, 0, skb);341 342 return brcmf_fws_process_skb(ifp, skb);343}344 345static int346brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset,347 struct sk_buff *pktbuf)348{349 brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf);350 return brcmf_bus_txdata(drvr->bus_if, pktbuf);351}352 353void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state)354{355 struct brcmf_bus *bus_if = dev_get_drvdata(dev);356 struct brcmf_pub *drvr = bus_if->drvr;357 358 brcmf_dbg(TRACE, "Enter\n");359 360 brcmf_fws_bus_blocked(drvr, state);361}362 363void364brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp,365 bool success)366{367 struct brcmf_bus *bus_if = dev_get_drvdata(dev);368 struct brcmf_bcdc *bcdc = bus_if->drvr->proto->pd;369 struct brcmf_if *ifp;370 371 /* await txstatus signal for firmware if active */372 if (brcmf_fws_fc_active(bcdc->fws)) {373 brcmf_fws_bustxcomplete(bcdc->fws, txp, success);374 } else {375 if (brcmf_proto_bcdc_hdrpull(bus_if->drvr, false, txp, &ifp))376 brcmu_pkt_buf_free_skb(txp);377 else378 brcmf_txfinalize(ifp, txp, success);379 }380}381 382static void383brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,384 enum proto_addr_mode addr_mode)385{386}387 388static void389brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx,390 u8 peer[ETH_ALEN])391{392}393 394static void395brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx,396 u8 peer[ETH_ALEN])397{398}399 400static void brcmf_proto_bcdc_rxreorder(struct brcmf_if *ifp,401 struct sk_buff *skb)402{403 brcmf_fws_rxreorder(ifp, skb);404}405 406static void407brcmf_proto_bcdc_add_if(struct brcmf_if *ifp)408{409 brcmf_fws_add_interface(ifp);410}411 412static void413brcmf_proto_bcdc_del_if(struct brcmf_if *ifp)414{415 brcmf_fws_del_interface(ifp);416}417 418static void419brcmf_proto_bcdc_reset_if(struct brcmf_if *ifp)420{421 brcmf_fws_reset_interface(ifp);422}423 424static int425brcmf_proto_bcdc_init_done(struct brcmf_pub *drvr)426{427 struct brcmf_bcdc *bcdc = drvr->proto->pd;428 struct brcmf_fws_info *fws;429 430 fws = brcmf_fws_attach(drvr);431 if (IS_ERR(fws))432 return PTR_ERR(fws);433 434 bcdc->fws = fws;435 return 0;436}437 438static void brcmf_proto_bcdc_debugfs_create(struct brcmf_pub *drvr)439{440 brcmf_fws_debugfs_create(drvr);441}442 443int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)444{445 struct brcmf_bcdc *bcdc;446 447 bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC);448 if (!bcdc)449 goto fail;450 451 /* ensure that the msg buf directly follows the cdc msg struct */452 if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) {453 bphy_err(drvr, "struct brcmf_proto_bcdc is not correctly defined\n");454 goto fail;455 }456 457 drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull;458 drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd;459 drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd;460 drvr->proto->tx_queue_data = brcmf_proto_bcdc_tx_queue_data;461 drvr->proto->txdata = brcmf_proto_bcdc_txdata;462 drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;463 drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;464 drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;465 drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder;466 drvr->proto->add_if = brcmf_proto_bcdc_add_if;467 drvr->proto->del_if = brcmf_proto_bcdc_del_if;468 drvr->proto->reset_if = brcmf_proto_bcdc_reset_if;469 drvr->proto->init_done = brcmf_proto_bcdc_init_done;470 drvr->proto->debugfs_create = brcmf_proto_bcdc_debugfs_create;471 drvr->proto->pd = bcdc;472 473 drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;474 drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +475 sizeof(struct brcmf_proto_bcdc_dcmd) + ROUND_UP_MARGIN;476 return 0;477 478fail:479 kfree(bcdc);480 return -ENOMEM;481}482 483void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)484{485 struct brcmf_bcdc *bcdc = drvr->proto->pd;486 487 drvr->proto->pd = NULL;488 brcmf_fws_detach(bcdc->fws);489 kfree(bcdc);490}491