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}