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 "fwil.h"
  19#include "proto.h"
  20
  21
  22#define MAX_HEX_DUMP_LEN        64
  23
  24#ifdef DEBUG
  25static const char * const brcmf_fil_errstr[] = {
  26        "BCME_OK",
  27        "BCME_ERROR",
  28        "BCME_BADARG",
  29        "BCME_BADOPTION",
  30        "BCME_NOTUP",
  31        "BCME_NOTDOWN",
  32        "BCME_NOTAP",
  33        "BCME_NOTSTA",
  34        "BCME_BADKEYIDX",
  35        "BCME_RADIOOFF",
  36        "BCME_NOTBANDLOCKED",
  37        "BCME_NOCLK",
  38        "BCME_BADRATESET",
  39        "BCME_BADBAND",
  40        "BCME_BUFTOOSHORT",
  41        "BCME_BUFTOOLONG",
  42        "BCME_BUSY",
  43        "BCME_NOTASSOCIATED",
  44        "BCME_BADSSIDLEN",
  45        "BCME_OUTOFRANGECHAN",
  46        "BCME_BADCHAN",
  47        "BCME_BADADDR",
  48        "BCME_NORESOURCE",
  49        "BCME_UNSUPPORTED",
  50        "BCME_BADLEN",
  51        "BCME_NOTREADY",
  52        "BCME_EPERM",
  53        "BCME_NOMEM",
  54        "BCME_ASSOCIATED",
  55        "BCME_RANGE",
  56        "BCME_NOTFOUND",
  57        "BCME_WME_NOT_ENABLED",
  58        "BCME_TSPEC_NOTFOUND",
  59        "BCME_ACM_NOTSUPPORTED",
  60        "BCME_NOT_WME_ASSOCIATION",
  61        "BCME_SDIO_ERROR",
  62        "BCME_DONGLE_DOWN",
  63        "BCME_VERSION",
  64        "BCME_TXFAIL",
  65        "BCME_RXFAIL",
  66        "BCME_NODEVICE",
  67        "BCME_NMODE_DISABLED",
  68        "BCME_NONRESIDENT",
  69        "BCME_SCANREJECT",
  70        "BCME_USAGE_ERROR",
  71        "BCME_IOCTL_ERROR",
  72        "BCME_SERIAL_PORT_ERR",
  73        "BCME_DISABLED",
  74        "BCME_DECERR",
  75        "BCME_ENCERR",
  76        "BCME_MICERR",
  77        "BCME_REPLAY",
  78        "BCME_IE_NOTFOUND",
  79};
  80
  81static const char *brcmf_fil_get_errstr(u32 err)
  82{
  83        if (err >= ARRAY_SIZE(brcmf_fil_errstr))
  84                return "(unknown)";
  85
  86        return brcmf_fil_errstr[err];
  87}
  88#else
  89static const char *brcmf_fil_get_errstr(u32 err)
  90{
  91        return "";
  92}
  93#endif /* DEBUG */
  94
  95static s32
  96brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
  97{
  98        struct brcmf_pub *drvr = ifp->drvr;
  99        s32 err, fwerr;
 100
 101        if (drvr->bus_if->state != BRCMF_BUS_UP) {
 102                bphy_err(drvr, "bus is down. we have nothing to do.\n");
 103                return -EIO;
 104        }
 105
 106        if (data != NULL)
 107                len = min_t(uint, len, BRCMF_DCMD_MAXLEN);
 108        if (set)
 109                err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd,
 110                                           data, len, &fwerr);
 111        else
 112                err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd,
 113                                             data, len, &fwerr);
 114
 115        if (err) {
 116                brcmf_dbg(FIL, "Failed: error=%d\n", err);
 117        } else if (fwerr < 0) {
 118                brcmf_dbg(FIL, "Firmware error: %s (%d)\n",
 119                          brcmf_fil_get_errstr((u32)(-fwerr)), fwerr);
 120                err = -EBADE;
 121        }
 122        if (ifp->fwil_fwerr)
 123                return fwerr;
 124
 125        return err;
 126}
 127
 128s32
 129brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
 130{
 131        s32 err;
 132
 133        mutex_lock(&ifp->drvr->proto_block);
 134
 135        brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
 136        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 137                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 138
 139        err = brcmf_fil_cmd_data(ifp, cmd, data, len, true);
 140        mutex_unlock(&ifp->drvr->proto_block);
 141
 142        return err;
 143}
 144
 145s32
 146brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
 147{
 148        s32 err;
 149
 150        mutex_lock(&ifp->drvr->proto_block);
 151        err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
 152
 153        brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
 154        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 155                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 156
 157        mutex_unlock(&ifp->drvr->proto_block);
 158
 159        return err;
 160}
 161
 162
 163s32
 164brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data)
 165{
 166        s32 err;
 167        __le32 data_le = cpu_to_le32(data);
 168
 169        mutex_lock(&ifp->drvr->proto_block);
 170        brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data);
 171        err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true);
 172        mutex_unlock(&ifp->drvr->proto_block);
 173
 174        return err;
 175}
 176
 177s32
 178brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data)
 179{
 180        s32 err;
 181        __le32 data_le = cpu_to_le32(*data);
 182
 183        mutex_lock(&ifp->drvr->proto_block);
 184        err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false);
 185        mutex_unlock(&ifp->drvr->proto_block);
 186        *data = le32_to_cpu(data_le);
 187        brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data);
 188
 189        return err;
 190}
 191
 192static u32
 193brcmf_create_iovar(char *name, const char *data, u32 datalen,
 194                   char *buf, u32 buflen)
 195{
 196        u32 len;
 197
 198        len = strlen(name) + 1;
 199
 200        if ((len + datalen) > buflen)
 201                return 0;
 202
 203        memcpy(buf, name, len);
 204
 205        /* append data onto the end of the name string */
 206        if (data && datalen)
 207                memcpy(&buf[len], data, datalen);
 208
 209        return len + datalen;
 210}
 211
 212
 213s32
 214brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
 215                         u32 len)
 216{
 217        struct brcmf_pub *drvr = ifp->drvr;
 218        s32 err;
 219        u32 buflen;
 220
 221        mutex_lock(&drvr->proto_block);
 222
 223        brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
 224        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 225                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 226
 227        buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
 228                                    sizeof(drvr->proto_buf));
 229        if (buflen) {
 230                err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
 231                                         buflen, true);
 232        } else {
 233                err = -EPERM;
 234                bphy_err(drvr, "Creating iovar failed\n");
 235        }
 236
 237        mutex_unlock(&drvr->proto_block);
 238        return err;
 239}
 240
 241s32
 242brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
 243                         u32 len)
 244{
 245        struct brcmf_pub *drvr = ifp->drvr;
 246        s32 err;
 247        u32 buflen;
 248
 249        mutex_lock(&drvr->proto_block);
 250
 251        buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
 252                                    sizeof(drvr->proto_buf));
 253        if (buflen) {
 254                err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
 255                                         buflen, false);
 256                if (err == 0)
 257                        memcpy(data, drvr->proto_buf, len);
 258        } else {
 259                err = -EPERM;
 260                bphy_err(drvr, "Creating iovar failed\n");
 261        }
 262
 263        brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
 264        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 265                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 266
 267        mutex_unlock(&drvr->proto_block);
 268        return err;
 269}
 270
 271s32
 272brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data)
 273{
 274        __le32 data_le = cpu_to_le32(data);
 275
 276        return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le));
 277}
 278
 279s32
 280brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data)
 281{
 282        __le32 data_le = cpu_to_le32(*data);
 283        s32 err;
 284
 285        err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le));
 286        if (err == 0)
 287                *data = le32_to_cpu(data_le);
 288        return err;
 289}
 290
 291static u32
 292brcmf_create_bsscfg(s32 bsscfgidx, char *name, char *data, u32 datalen,
 293                    char *buf, u32 buflen)
 294{
 295        const s8 *prefix = "bsscfg:";
 296        s8 *p;
 297        u32 prefixlen;
 298        u32 namelen;
 299        u32 iolen;
 300        __le32 bsscfgidx_le;
 301
 302        if (bsscfgidx == 0)
 303                return brcmf_create_iovar(name, data, datalen, buf, buflen);
 304
 305        prefixlen = strlen(prefix);
 306        namelen = strlen(name) + 1; /* length of iovar  name + null */
 307        iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen;
 308
 309        if (buflen < iolen) {
 310                brcmf_err("buffer is too short\n");
 311                return 0;
 312        }
 313
 314        p = buf;
 315
 316        /* copy prefix, no null */
 317        memcpy(p, prefix, prefixlen);
 318        p += prefixlen;
 319
 320        /* copy iovar name including null */
 321        memcpy(p, name, namelen);
 322        p += namelen;
 323
 324        /* bss config index as first data */
 325        bsscfgidx_le = cpu_to_le32(bsscfgidx);
 326        memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le));
 327        p += sizeof(bsscfgidx_le);
 328
 329        /* parameter buffer follows */
 330        if (datalen)
 331                memcpy(p, data, datalen);
 332
 333        return iolen;
 334}
 335
 336s32
 337brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name,
 338                          void *data, u32 len)
 339{
 340        struct brcmf_pub *drvr = ifp->drvr;
 341        s32 err;
 342        u32 buflen;
 343
 344        mutex_lock(&drvr->proto_block);
 345
 346        brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
 347                  ifp->bsscfgidx, name, len);
 348        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 349                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 350
 351        buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
 352                                     drvr->proto_buf, sizeof(drvr->proto_buf));
 353        if (buflen) {
 354                err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
 355                                         buflen, true);
 356        } else {
 357                err = -EPERM;
 358                bphy_err(drvr, "Creating bsscfg failed\n");
 359        }
 360
 361        mutex_unlock(&drvr->proto_block);
 362        return err;
 363}
 364
 365s32
 366brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
 367                          void *data, u32 len)
 368{
 369        struct brcmf_pub *drvr = ifp->drvr;
 370        s32 err;
 371        u32 buflen;
 372
 373        mutex_lock(&drvr->proto_block);
 374
 375        buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
 376                                     drvr->proto_buf, sizeof(drvr->proto_buf));
 377        if (buflen) {
 378                err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
 379                                         buflen, false);
 380                if (err == 0)
 381                        memcpy(data, drvr->proto_buf, len);
 382        } else {
 383                err = -EPERM;
 384                bphy_err(drvr, "Creating bsscfg failed\n");
 385        }
 386        brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
 387                  ifp->bsscfgidx, name, len);
 388        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
 389                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 390
 391        mutex_unlock(&drvr->proto_block);
 392        return err;
 393
 394}
 395
 396s32
 397brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data)
 398{
 399        __le32 data_le = cpu_to_le32(data);
 400
 401        return brcmf_fil_bsscfg_data_set(ifp, name, &data_le,
 402                                         sizeof(data_le));
 403}
 404
 405s32
 406brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data)
 407{
 408        __le32 data_le = cpu_to_le32(*data);
 409        s32 err;
 410
 411        err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le,
 412                                        sizeof(data_le));
 413        if (err == 0)
 414                *data = le32_to_cpu(data_le);
 415        return err;
 416}
 417