linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
<<
>>
Prefs
   1// SPDX-License-Identifier: ISC
   2/*
   3 * Copyright (c) 2012 Broadcom Corporation
   4 */
   5
   6/* FWIL is the Firmware Interface Layer. In this module the support functions
   7 * are located to set and get variables to and from the firmware.
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/netdevice.h>
  12#include <brcmu_utils.h>
  13#include <brcmu_wifi.h>
  14#include "core.h"
  15#include "bus.h"
  16#include "debug.h"
  17#include "tracepoint.h"
  18#include "xtlv.h"
  19#include "fwil.h"
  20#include "proto.h"
  21
  22
  23#define MAX_HEX_DUMP_LEN        64
  24
  25#ifdef DEBUG
  26static const char * const brcmf_fil_errstr[] = {
  27        "BCME_OK",
  28        "BCME_ERROR",
  29        "BCME_BADARG",
  30        "BCME_BADOPTION",
  31        "BCME_NOTUP",
  32        "BCME_NOTDOWN",
  33        "BCME_NOTAP",
  34        "BCME_NOTSTA",
  35        "BCME_BADKEYIDX",
  36        "BCME_RADIOOFF",
  37        "BCME_NOTBANDLOCKED",
  38        "BCME_NOCLK",
  39        "BCME_BADRATESET",
  40        "BCME_BADBAND",
  41        "BCME_BUFTOOSHORT",
  42        "BCME_BUFTOOLONG",
  43        "BCME_BUSY",
  44        "BCME_NOTASSOCIATED",
  45        "BCME_BADSSIDLEN",
  46        "BCME_OUTOFRANGECHAN",
  47        "BCME_BADCHAN",
  48        "BCME_BADADDR",
  49        "BCME_NORESOURCE",
  50        "BCME_UNSUPPORTED",
  51        "BCME_BADLEN",
  52        "BCME_NOTREADY",
  53        "BCME_EPERM",
  54        "BCME_NOMEM",
  55        "BCME_ASSOCIATED",
  56        "BCME_RANGE",
  57        "BCME_NOTFOUND",
  58        "BCME_WME_NOT_ENABLED",
  59        "BCME_TSPEC_NOTFOUND",
  60        "BCME_ACM_NOTSUPPORTED",
  61        "BCME_NOT_WME_ASSOCIATION",
  62        "BCME_SDIO_ERROR",
  63        "BCME_DONGLE_DOWN",
  64        "BCME_VERSION",
  65        "BCME_TXFAIL",
  66        "BCME_RXFAIL",
  67        "BCME_NODEVICE",
  68        "BCME_NMODE_DISABLED",
  69        "BCME_NONRESIDENT",
  70        "BCME_SCANREJECT",
  71        "BCME_USAGE_ERROR",
  72        "BCME_IOCTL_ERROR",
  73        "BCME_SERIAL_PORT_ERR",
  74        "BCME_DISABLED",
  75        "BCME_DECERR",
  76        "BCME_ENCERR",
  77        "BCME_MICERR",
  78        "BCME_REPLAY",
  79        "BCME_IE_NOTFOUND",
  80};
  81
  82static const char *brcmf_fil_get_errstr(u32 err)
  83{
  84        if (err >= ARRAY_SIZE(brcmf_fil_errstr))
  85                return "(unknown)";
  86
  87        return brcmf_fil_errstr[err];
  88}
  89#else
  90static const char *brcmf_fil_get_errstr(u32 err)
  91{
  92        return "";
  93}
  94#endif /* DEBUG */
  95
  96static s32
  97brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
  98{
  99        struct brcmf_pub *drvr = ifp->drvr;
 100        s32 err, fwerr;
 101
 102        if (drvr->bus_if->state != BRCMF_BUS_UP) {
 103                bphy_err(drvr, "bus is down. we have nothing to do.\n");
 104                return -EIO;
 105        }
 106
 107        if (data != NULL)
 108                len = min_t(uint, len, BRCMF_DCMD_MAXLEN);
 109        if (set)
 110                err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd,
 111                                           data, len, &fwerr);
 112        else
 113                err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd,
 114                                             data, len, &fwerr);
 115
 116        if (err) {
 117                brcmf_dbg(FIL, "Failed: error=%d\n", err);
 118        } else if (fwerr < 0) {
 119                brcmf_dbg(FIL, "Firmware error: %s (%d)\n",
 120                          brcmf_fil_get_errstr((u32)(-fwerr)), fwerr);
 121                err = -EBADE;
 122        }
 123        if (ifp->fwil_fwerr)
 124                return fwerr;
 125
 126        return err;
 127}
 128
 129s32
 130brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
 131{
 132        s32 err;
 133
 134        mutex_lock(&ifp->drvr->proto_block);
 135
 136        brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
 137        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 138                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 139
 140        err = brcmf_fil_cmd_data(ifp, cmd, data, len, true);
 141        mutex_unlock(&ifp->drvr->proto_block);
 142
 143        return err;
 144}
 145
 146s32
 147brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
 148{
 149        s32 err;
 150
 151        mutex_lock(&ifp->drvr->proto_block);
 152        err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
 153
 154        brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d, err=%d\n", ifp->ifidx, cmd,
 155                  len, err);
 156        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 157                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 158
 159        mutex_unlock(&ifp->drvr->proto_block);
 160
 161        return err;
 162}
 163
 164
 165s32
 166brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data)
 167{
 168        s32 err;
 169        __le32 data_le = cpu_to_le32(data);
 170
 171        mutex_lock(&ifp->drvr->proto_block);
 172        brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data);
 173        err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true);
 174        mutex_unlock(&ifp->drvr->proto_block);
 175
 176        return err;
 177}
 178
 179s32
 180brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data)
 181{
 182        s32 err;
 183        __le32 data_le = cpu_to_le32(*data);
 184
 185        mutex_lock(&ifp->drvr->proto_block);
 186        err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false);
 187        mutex_unlock(&ifp->drvr->proto_block);
 188        *data = le32_to_cpu(data_le);
 189        brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data);
 190
 191        return err;
 192}
 193
 194static u32
 195brcmf_create_iovar(char *name, const char *data, u32 datalen,
 196                   char *buf, u32 buflen)
 197{
 198        u32 len;
 199
 200        len = strlen(name) + 1;
 201
 202        if ((len + datalen) > buflen)
 203                return 0;
 204
 205        memcpy(buf, name, len);
 206
 207        /* append data onto the end of the name string */
 208        if (data && datalen)
 209                memcpy(&buf[len], data, datalen);
 210
 211        return len + datalen;
 212}
 213
 214
 215s32
 216brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
 217                         u32 len)
 218{
 219        struct brcmf_pub *drvr = ifp->drvr;
 220        s32 err;
 221        u32 buflen;
 222
 223        mutex_lock(&drvr->proto_block);
 224
 225        brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
 226        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 227                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 228
 229        buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
 230                                    sizeof(drvr->proto_buf));
 231        if (buflen) {
 232                err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
 233                                         buflen, true);
 234        } else {
 235                err = -EPERM;
 236                bphy_err(drvr, "Creating iovar failed\n");
 237        }
 238
 239        mutex_unlock(&drvr->proto_block);
 240        return err;
 241}
 242
 243s32
 244brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
 245                         u32 len)
 246{
 247        struct brcmf_pub *drvr = ifp->drvr;
 248        s32 err;
 249        u32 buflen;
 250
 251        mutex_lock(&drvr->proto_block);
 252
 253        buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
 254                                    sizeof(drvr->proto_buf));
 255        if (buflen) {
 256                err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
 257                                         buflen, false);
 258                if (err == 0)
 259                        memcpy(data, drvr->proto_buf, len);
 260        } else {
 261                err = -EPERM;
 262                bphy_err(drvr, "Creating iovar failed\n");
 263        }
 264
 265        brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d, err=%d\n", ifp->ifidx, name,
 266                  len, err);
 267        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 268                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 269
 270        mutex_unlock(&drvr->proto_block);
 271        return err;
 272}
 273
 274s32
 275brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data)
 276{
 277        __le32 data_le = cpu_to_le32(data);
 278
 279        return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le));
 280}
 281
 282s32
 283brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data)
 284{
 285        __le32 data_le = cpu_to_le32(*data);
 286        s32 err;
 287
 288        err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le));
 289        if (err == 0)
 290                *data = le32_to_cpu(data_le);
 291        return err;
 292}
 293
 294static u32
 295brcmf_create_bsscfg(s32 bsscfgidx, char *name, char *data, u32 datalen,
 296                    char *buf, u32 buflen)
 297{
 298        const s8 *prefix = "bsscfg:";
 299        s8 *p;
 300        u32 prefixlen;
 301        u32 namelen;
 302        u32 iolen;
 303        __le32 bsscfgidx_le;
 304
 305        if (bsscfgidx == 0)
 306                return brcmf_create_iovar(name, data, datalen, buf, buflen);
 307
 308        prefixlen = strlen(prefix);
 309        namelen = strlen(name) + 1; /* length of iovar  name + null */
 310        iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen;
 311
 312        if (buflen < iolen) {
 313                brcmf_err("buffer is too short\n");
 314                return 0;
 315        }
 316
 317        p = buf;
 318
 319        /* copy prefix, no null */
 320        memcpy(p, prefix, prefixlen);
 321        p += prefixlen;
 322
 323        /* copy iovar name including null */
 324        memcpy(p, name, namelen);
 325        p += namelen;
 326
 327        /* bss config index as first data */
 328        bsscfgidx_le = cpu_to_le32(bsscfgidx);
 329        memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le));
 330        p += sizeof(bsscfgidx_le);
 331
 332        /* parameter buffer follows */
 333        if (datalen)
 334                memcpy(p, data, datalen);
 335
 336        return iolen;
 337}
 338
 339s32
 340brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name,
 341                          void *data, u32 len)
 342{
 343        struct brcmf_pub *drvr = ifp->drvr;
 344        s32 err;
 345        u32 buflen;
 346
 347        mutex_lock(&drvr->proto_block);
 348
 349        brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
 350                  ifp->bsscfgidx, name, len);
 351        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 352                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 353
 354        buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
 355                                     drvr->proto_buf, sizeof(drvr->proto_buf));
 356        if (buflen) {
 357                err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
 358                                         buflen, true);
 359        } else {
 360                err = -EPERM;
 361                bphy_err(drvr, "Creating bsscfg failed\n");
 362        }
 363
 364        mutex_unlock(&drvr->proto_block);
 365        return err;
 366}
 367
 368s32
 369brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
 370                          void *data, u32 len)
 371{
 372        struct brcmf_pub *drvr = ifp->drvr;
 373        s32 err;
 374        u32 buflen;
 375
 376        mutex_lock(&drvr->proto_block);
 377
 378        buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
 379                                     drvr->proto_buf, sizeof(drvr->proto_buf));
 380        if (buflen) {
 381                err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
 382                                         buflen, false);
 383                if (err == 0)
 384                        memcpy(data, drvr->proto_buf, len);
 385        } else {
 386                err = -EPERM;
 387                bphy_err(drvr, "Creating bsscfg failed\n");
 388        }
 389        brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d, err=%d\n",
 390                  ifp->ifidx, ifp->bsscfgidx, name, len, err);
 391        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 392                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 393
 394        mutex_unlock(&drvr->proto_block);
 395        return err;
 396}
 397
 398s32
 399brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data)
 400{
 401        __le32 data_le = cpu_to_le32(data);
 402
 403        return brcmf_fil_bsscfg_data_set(ifp, name, &data_le,
 404                                         sizeof(data_le));
 405}
 406
 407s32
 408brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data)
 409{
 410        __le32 data_le = cpu_to_le32(*data);
 411        s32 err;
 412
 413        err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le,
 414                                        sizeof(data_le));
 415        if (err == 0)
 416                *data = le32_to_cpu(data_le);
 417        return err;
 418}
 419
 420static u32 brcmf_create_xtlv(char *name, u16 id, char *data, u32 len,
 421                             char *buf, u32 buflen)
 422{
 423        u32 iolen;
 424        u32 nmlen;
 425
 426        nmlen = strlen(name) + 1;
 427        iolen = nmlen + brcmf_xtlv_data_size(len, BRCMF_XTLV_OPTION_ALIGN32);
 428
 429        if (iolen > buflen) {
 430                brcmf_err("buffer is too short\n");
 431                return 0;
 432        }
 433
 434        memcpy(buf, name, nmlen);
 435        brcmf_xtlv_pack_header((void *)(buf + nmlen), id, len, data,
 436                               BRCMF_XTLV_OPTION_ALIGN32);
 437
 438        return iolen;
 439}
 440
 441s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, char *name, u16 id,
 442                            void *data, u32 len)
 443{
 444        struct brcmf_pub *drvr = ifp->drvr;
 445        s32 err;
 446        u32 buflen;
 447
 448        mutex_lock(&drvr->proto_block);
 449
 450        brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u\n", ifp->ifidx, name,
 451                  id, len);
 452        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 453                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 454
 455        buflen = brcmf_create_xtlv(name, id, data, len,
 456                                   drvr->proto_buf, sizeof(drvr->proto_buf));
 457        if (buflen) {
 458                err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
 459                                         buflen, true);
 460        } else {
 461                err = -EPERM;
 462                bphy_err(drvr, "Creating xtlv failed\n");
 463        }
 464
 465        mutex_unlock(&drvr->proto_block);
 466        return err;
 467}
 468
 469s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, char *name, u16 id,
 470                            void *data, u32 len)
 471{
 472        struct brcmf_pub *drvr = ifp->drvr;
 473        s32 err;
 474        u32 buflen;
 475
 476        mutex_lock(&drvr->proto_block);
 477
 478        buflen = brcmf_create_xtlv(name, id, data, len,
 479                                   drvr->proto_buf, sizeof(drvr->proto_buf));
 480        if (buflen) {
 481                err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
 482                                         buflen, false);
 483                if (err == 0)
 484                        memcpy(data, drvr->proto_buf, len);
 485        } else {
 486                err = -EPERM;
 487                bphy_err(drvr, "Creating bsscfg failed\n");
 488        }
 489        brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u, err=%d\n",
 490                  ifp->ifidx, name, id, len, err);
 491        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 492                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 493
 494        mutex_unlock(&drvr->proto_block);
 495        return err;
 496}
 497
 498s32 brcmf_fil_xtlv_int_set(struct brcmf_if *ifp, char *name, u16 id, u32 data)
 499{
 500        __le32 data_le = cpu_to_le32(data);
 501
 502        return brcmf_fil_xtlv_data_set(ifp, name, id, &data_le,
 503                                         sizeof(data_le));
 504}
 505
 506s32 brcmf_fil_xtlv_int_get(struct brcmf_if *ifp, char *name, u16 id, u32 *data)
 507{
 508        __le32 data_le = cpu_to_le32(*data);
 509        s32 err;
 510
 511        err = brcmf_fil_xtlv_data_get(ifp, name, id, &data_le, sizeof(data_le));
 512        if (err == 0)
 513                *data = le32_to_cpu(data_le);
 514        return err;
 515}
 516
 517s32 brcmf_fil_xtlv_int8_get(struct brcmf_if *ifp, char *name, u16 id, u8 *data)
 518{
 519        return brcmf_fil_xtlv_data_get(ifp, name, id, data, sizeof(*data));
 520}
 521
 522s32 brcmf_fil_xtlv_int16_get(struct brcmf_if *ifp, char *name, u16 id, u16 *data)
 523{
 524        __le16 data_le = cpu_to_le16(*data);
 525        s32 err;
 526
 527        err = brcmf_fil_xtlv_data_get(ifp, name, id, &data_le, sizeof(data_le));
 528        if (err == 0)
 529                *data = le16_to_cpu(data_le);
 530        return err;
 531}
 532
 533