linux/drivers/net/wireless/ath/wil6210/fw_inc.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
   3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
   4 *
   5 * Permission to use, copy, modify, and/or distribute this software for any
   6 * purpose with or without fee is hereby granted, provided that the above
   7 * copyright notice and this permission notice appear in all copies.
   8 *
   9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16 */
  17
  18/* Algorithmic part of the firmware download.
  19 * To be included in the container file providing framework
  20 */
  21
  22#define wil_err_fw(wil, fmt, arg...) wil_err(wil, "ERR[ FW ]" fmt, ##arg)
  23#define wil_dbg_fw(wil, fmt, arg...) wil_dbg(wil, "DBG[ FW ]" fmt, ##arg)
  24#define wil_hex_dump_fw(prefix_str, prefix_type, rowsize,               \
  25                        groupsize, buf, len, ascii)                     \
  26                        print_hex_dump_debug("DBG[ FW ]" prefix_str,    \
  27                                             prefix_type, rowsize,      \
  28                                             groupsize, buf, len, ascii)
  29
  30static bool wil_fw_addr_check(struct wil6210_priv *wil,
  31                              void __iomem **ioaddr, __le32 val,
  32                              u32 size, const char *msg)
  33{
  34        *ioaddr = wmi_buffer_block(wil, val, size);
  35        if (!(*ioaddr)) {
  36                wil_err_fw(wil, "bad %s: 0x%08x\n", msg, le32_to_cpu(val));
  37                return false;
  38        }
  39        return true;
  40}
  41
  42/**
  43 * wil_fw_verify - verify firmware file validity
  44 *
  45 * perform various checks for the firmware file header.
  46 * records are not validated.
  47 *
  48 * Return file size or negative error
  49 */
  50static int wil_fw_verify(struct wil6210_priv *wil, const u8 *data, size_t size)
  51{
  52        const struct wil_fw_record_head *hdr = (const void *)data;
  53        struct wil_fw_record_file_header fh;
  54        const struct wil_fw_record_file_header *fh_;
  55        u32 crc;
  56        u32 dlen;
  57
  58        if (size % 4) {
  59                wil_err_fw(wil, "image size not aligned: %zu\n", size);
  60                return -EINVAL;
  61        }
  62        /* have enough data for the file header? */
  63        if (size < sizeof(*hdr) + sizeof(fh)) {
  64                wil_err_fw(wil, "file too short: %zu bytes\n", size);
  65                return -EINVAL;
  66        }
  67
  68        /* start with the file header? */
  69        if (le16_to_cpu(hdr->type) != wil_fw_type_file_header) {
  70                wil_err_fw(wil, "no file header\n");
  71                return -EINVAL;
  72        }
  73
  74        /* data_len */
  75        fh_ = (struct wil_fw_record_file_header *)&hdr[1];
  76        dlen = le32_to_cpu(fh_->data_len);
  77        if (dlen % 4) {
  78                wil_err_fw(wil, "data length not aligned: %lu\n", (ulong)dlen);
  79                return -EINVAL;
  80        }
  81        if (size < dlen) {
  82                wil_err_fw(wil, "file truncated at %zu/%lu\n",
  83                           size, (ulong)dlen);
  84                return -EINVAL;
  85        }
  86        if (dlen < sizeof(*hdr) + sizeof(fh)) {
  87                wil_err_fw(wil, "data length too short: %lu\n", (ulong)dlen);
  88                return -EINVAL;
  89        }
  90
  91        /* signature */
  92        if (le32_to_cpu(fh_->signature) != WIL_FW_SIGNATURE) {
  93                wil_err_fw(wil, "bad header signature: 0x%08x\n",
  94                           le32_to_cpu(fh_->signature));
  95                return -EINVAL;
  96        }
  97
  98        /* version */
  99        if (le32_to_cpu(fh_->version) > WIL_FW_FMT_VERSION) {
 100                wil_err_fw(wil, "unsupported header version: %d\n",
 101                           le32_to_cpu(fh_->version));
 102                return -EINVAL;
 103        }
 104
 105        /* checksum. ~crc32(~0, data, size) when fh.crc set to 0*/
 106        fh = *fh_;
 107        fh.crc = 0;
 108
 109        crc = crc32_le(~0, (unsigned char const *)hdr, sizeof(*hdr));
 110        crc = crc32_le(crc, (unsigned char const *)&fh, sizeof(fh));
 111        crc = crc32_le(crc, (unsigned char const *)&fh_[1],
 112                       dlen - sizeof(*hdr) - sizeof(fh));
 113        crc = ~crc;
 114
 115        if (crc != le32_to_cpu(fh_->crc)) {
 116                wil_err_fw(wil, "checksum mismatch:"
 117                           " calculated for %lu bytes 0x%08x != 0x%08x\n",
 118                           (ulong)dlen, crc, le32_to_cpu(fh_->crc));
 119                return -EINVAL;
 120        }
 121
 122        return (int)dlen;
 123}
 124
 125static int fw_ignore_section(struct wil6210_priv *wil, const void *data,
 126                             size_t size)
 127{
 128        return 0;
 129}
 130
 131static int
 132fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
 133                       size_t size)
 134{
 135        const struct wil_fw_record_capabilities *rec = data;
 136        size_t capa_size;
 137
 138        if (size < sizeof(*rec)) {
 139                wil_err_fw(wil, "capabilities record too short: %zu\n", size);
 140                /* let the FW load anyway */
 141                return 0;
 142        }
 143
 144        capa_size = size - offsetof(struct wil_fw_record_capabilities,
 145                                    capabilities);
 146        bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
 147        memcpy(wil->fw_capabilities, rec->capabilities,
 148               min_t(size_t, sizeof(wil->fw_capabilities), capa_size));
 149        wil_hex_dump_fw("CAPA", DUMP_PREFIX_OFFSET, 16, 1,
 150                        rec->capabilities, capa_size, false);
 151        return 0;
 152}
 153
 154static int
 155fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
 156                   size_t size)
 157{
 158        const struct wil_fw_record_brd_file *rec = data;
 159
 160        if (size < sizeof(*rec)) {
 161                wil_err_fw(wil, "brd_file record too short: %zu\n", size);
 162                return 0;
 163        }
 164
 165        wil->brd_file_addr = le32_to_cpu(rec->base_addr);
 166        wil->brd_file_max_size = le32_to_cpu(rec->max_size_bytes);
 167
 168        wil_dbg_fw(wil, "brd_file_addr 0x%x, brd_file_max_size %d\n",
 169                   wil->brd_file_addr, wil->brd_file_max_size);
 170
 171        return 0;
 172}
 173
 174static int
 175fw_handle_concurrency(struct wil6210_priv *wil, const void *data,
 176                      size_t size)
 177{
 178        const struct wil_fw_record_concurrency *rec = data;
 179        const struct wil_fw_concurrency_combo *combo;
 180        const struct wil_fw_concurrency_limit *limit;
 181        size_t remain, lsize;
 182        int i, n_combos;
 183
 184        if (size < sizeof(*rec)) {
 185                wil_err_fw(wil, "concurrency record too short: %zu\n", size);
 186                /* continue, let the FW load anyway */
 187                return 0;
 188        }
 189
 190        n_combos = le16_to_cpu(rec->n_combos);
 191        remain = size - offsetof(struct wil_fw_record_concurrency, combos);
 192        combo = rec->combos;
 193        for (i = 0; i < n_combos; i++) {
 194                if (remain < sizeof(*combo))
 195                        goto out_short;
 196                remain -= sizeof(*combo);
 197                limit = combo->limits;
 198                lsize = combo->n_limits * sizeof(*limit);
 199                if (remain < lsize)
 200                        goto out_short;
 201                remain -= lsize;
 202                limit += combo->n_limits;
 203                combo = (struct wil_fw_concurrency_combo *)limit;
 204        }
 205
 206        return wil_cfg80211_iface_combinations_from_fw(wil, rec);
 207out_short:
 208        wil_err_fw(wil, "concurrency record truncated\n");
 209        return 0;
 210}
 211
 212static int
 213fw_handle_comment(struct wil6210_priv *wil, const void *data,
 214                  size_t size)
 215{
 216        const struct wil_fw_record_comment_hdr *hdr = data;
 217        u32 magic;
 218        int rc = 0;
 219
 220        if (size < sizeof(*hdr))
 221                return 0;
 222
 223        magic = le32_to_cpu(hdr->magic);
 224
 225        switch (magic) {
 226        case WIL_FW_CAPABILITIES_MAGIC:
 227                wil_dbg_fw(wil, "magic is WIL_FW_CAPABILITIES_MAGIC\n");
 228                rc = fw_handle_capabilities(wil, data, size);
 229                break;
 230        case WIL_BRD_FILE_MAGIC:
 231                wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n");
 232                rc = fw_handle_brd_file(wil, data, size);
 233                break;
 234        case WIL_FW_CONCURRENCY_MAGIC:
 235                wil_dbg_fw(wil, "magic is WIL_FW_CONCURRENCY_MAGIC\n");
 236                rc = fw_handle_concurrency(wil, data, size);
 237                break;
 238        default:
 239                wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
 240                                data, size, true);
 241        }
 242
 243        return rc;
 244}
 245
 246static int __fw_handle_data(struct wil6210_priv *wil, const void *data,
 247                            size_t size, __le32 addr)
 248{
 249        const struct wil_fw_record_data *d = data;
 250        void __iomem *dst;
 251        size_t s = size - sizeof(*d);
 252
 253        if (size < sizeof(*d) + sizeof(u32)) {
 254                wil_err_fw(wil, "data record too short: %zu\n", size);
 255                return -EINVAL;
 256        }
 257
 258        if (!wil_fw_addr_check(wil, &dst, addr, s, "address"))
 259                return -EINVAL;
 260        wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(addr), s);
 261        wil_memcpy_toio_32(dst, d->data, s);
 262        wmb(); /* finish before processing next record */
 263
 264        return 0;
 265}
 266
 267static int fw_handle_data(struct wil6210_priv *wil, const void *data,
 268                          size_t size)
 269{
 270        const struct wil_fw_record_data *d = data;
 271
 272        return __fw_handle_data(wil, data, size, d->addr);
 273}
 274
 275static int fw_handle_fill(struct wil6210_priv *wil, const void *data,
 276                          size_t size)
 277{
 278        const struct wil_fw_record_fill *d = data;
 279        void __iomem *dst;
 280        u32 v;
 281        size_t s = (size_t)le32_to_cpu(d->size);
 282
 283        if (size != sizeof(*d)) {
 284                wil_err_fw(wil, "bad size for fill record: %zu\n", size);
 285                return -EINVAL;
 286        }
 287
 288        if (s < sizeof(u32)) {
 289                wil_err_fw(wil, "fill size too short: %zu\n", s);
 290                return -EINVAL;
 291        }
 292
 293        if (s % sizeof(u32)) {
 294                wil_err_fw(wil, "fill size not aligned: %zu\n", s);
 295                return -EINVAL;
 296        }
 297
 298        if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
 299                return -EINVAL;
 300
 301        v = le32_to_cpu(d->value);
 302        wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n",
 303                   le32_to_cpu(d->addr), v, s);
 304        wil_memset_toio_32(dst, v, s);
 305        wmb(); /* finish before processing next record */
 306
 307        return 0;
 308}
 309
 310static int fw_handle_file_header(struct wil6210_priv *wil, const void *data,
 311                                 size_t size)
 312{
 313        const struct wil_fw_record_file_header *d = data;
 314
 315        if (size != sizeof(*d)) {
 316                wil_err_fw(wil, "file header length incorrect: %zu\n", size);
 317                return -EINVAL;
 318        }
 319
 320        wil_dbg_fw(wil, "new file, ver. %d, %i bytes\n",
 321                   d->version, d->data_len);
 322        wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, d->comment,
 323                        sizeof(d->comment), true);
 324
 325        if (!memcmp(d->comment, WIL_FW_VERSION_PREFIX,
 326                    WIL_FW_VERSION_PREFIX_LEN))
 327                memcpy(wil->fw_version,
 328                       d->comment + WIL_FW_VERSION_PREFIX_LEN,
 329                       min(sizeof(d->comment) - WIL_FW_VERSION_PREFIX_LEN,
 330                           sizeof(wil->fw_version) - 1));
 331
 332        return 0;
 333}
 334
 335static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data,
 336                                  size_t size)
 337{
 338        const struct wil_fw_record_direct_write *d = data;
 339        const struct wil_fw_data_dwrite *block = d->data;
 340        int n, i;
 341
 342        if (size % sizeof(*block)) {
 343                wil_err_fw(wil, "record size not aligned on %zu: %zu\n",
 344                           sizeof(*block), size);
 345                return -EINVAL;
 346        }
 347        n = size / sizeof(*block);
 348
 349        for (i = 0; i < n; i++) {
 350                void __iomem *dst;
 351                u32 m = le32_to_cpu(block[i].mask);
 352                u32 v = le32_to_cpu(block[i].value);
 353                u32 x, y;
 354
 355                if (!wil_fw_addr_check(wil, &dst, block[i].addr, 0, "address"))
 356                        return -EINVAL;
 357
 358                x = readl(dst);
 359                y = (x & m) | (v & ~m);
 360                wil_dbg_fw(wil, "write [0x%08x] <== 0x%08x "
 361                           "(old 0x%08x val 0x%08x mask 0x%08x)\n",
 362                           le32_to_cpu(block[i].addr), y, x, v, m);
 363                writel(y, dst);
 364                wmb(); /* finish before processing next record */
 365        }
 366
 367        return 0;
 368}
 369
 370static int gw_write(struct wil6210_priv *wil, void __iomem *gwa_addr,
 371                    void __iomem *gwa_cmd, void __iomem *gwa_ctl, u32 gw_cmd,
 372                    u32 a)
 373{
 374        unsigned delay = 0;
 375
 376        writel(a, gwa_addr);
 377        writel(gw_cmd, gwa_cmd);
 378        wmb(); /* finish before activate gw */
 379
 380        writel(WIL_FW_GW_CTL_RUN, gwa_ctl); /* activate gw */
 381        do {
 382                udelay(1); /* typical time is few usec */
 383                if (delay++ > 100) {
 384                        wil_err_fw(wil, "gw timeout\n");
 385                        return -EINVAL;
 386                }
 387        } while (readl(gwa_ctl) & WIL_FW_GW_CTL_BUSY); /* gw done? */
 388
 389        return 0;
 390}
 391
 392static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data,
 393                                  size_t size)
 394{
 395        const struct wil_fw_record_gateway_data *d = data;
 396        const struct wil_fw_data_gw *block = d->data;
 397        void __iomem *gwa_addr;
 398        void __iomem *gwa_val;
 399        void __iomem *gwa_cmd;
 400        void __iomem *gwa_ctl;
 401        u32 gw_cmd;
 402        int n, i;
 403
 404        if (size < sizeof(*d) + sizeof(*block)) {
 405                wil_err_fw(wil, "gateway record too short: %zu\n", size);
 406                return -EINVAL;
 407        }
 408
 409        if ((size - sizeof(*d)) % sizeof(*block)) {
 410                wil_err_fw(wil, "gateway record data size"
 411                           " not aligned on %zu: %zu\n",
 412                           sizeof(*block), size - sizeof(*d));
 413                return -EINVAL;
 414        }
 415        n = (size - sizeof(*d)) / sizeof(*block);
 416
 417        gw_cmd = le32_to_cpu(d->command);
 418
 419        wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n",
 420                   n, gw_cmd);
 421
 422        if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
 423                               "gateway_addr_addr") ||
 424            !wil_fw_addr_check(wil, &gwa_val, d->gateway_value_addr, 0,
 425                               "gateway_value_addr") ||
 426            !wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
 427                               "gateway_cmd_addr") ||
 428            !wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
 429                               "gateway_ctrl_address"))
 430                return -EINVAL;
 431
 432        wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x"
 433                   " cmd 0x%08x ctl 0x%08x\n",
 434                   le32_to_cpu(d->gateway_addr_addr),
 435                   le32_to_cpu(d->gateway_value_addr),
 436                   le32_to_cpu(d->gateway_cmd_addr),
 437                   le32_to_cpu(d->gateway_ctrl_address));
 438
 439        for (i = 0; i < n; i++) {
 440                int rc;
 441                u32 a = le32_to_cpu(block[i].addr);
 442                u32 v = le32_to_cpu(block[i].value);
 443
 444                wil_dbg_fw(wil, "  gw write[%3d] [0x%08x] <== 0x%08x\n",
 445                           i, a, v);
 446
 447                writel(v, gwa_val);
 448                rc = gw_write(wil, gwa_addr, gwa_cmd, gwa_ctl, gw_cmd, a);
 449                if (rc)
 450                        return rc;
 451        }
 452
 453        return 0;
 454}
 455
 456static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data,
 457                                   size_t size)
 458{
 459        const struct wil_fw_record_gateway_data4 *d = data;
 460        const struct wil_fw_data_gw4 *block = d->data;
 461        void __iomem *gwa_addr;
 462        void __iomem *gwa_val[ARRAY_SIZE(block->value)];
 463        void __iomem *gwa_cmd;
 464        void __iomem *gwa_ctl;
 465        u32 gw_cmd;
 466        int n, i, k;
 467
 468        if (size < sizeof(*d) + sizeof(*block)) {
 469                wil_err_fw(wil, "gateway4 record too short: %zu\n", size);
 470                return -EINVAL;
 471        }
 472
 473        if ((size - sizeof(*d)) % sizeof(*block)) {
 474                wil_err_fw(wil, "gateway4 record data size"
 475                           " not aligned on %zu: %zu\n",
 476                           sizeof(*block), size - sizeof(*d));
 477                return -EINVAL;
 478        }
 479        n = (size - sizeof(*d)) / sizeof(*block);
 480
 481        gw_cmd = le32_to_cpu(d->command);
 482
 483        wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n",
 484                   n, gw_cmd);
 485
 486        if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
 487                               "gateway_addr_addr"))
 488                return -EINVAL;
 489        for (k = 0; k < ARRAY_SIZE(block->value); k++)
 490                if (!wil_fw_addr_check(wil, &gwa_val[k],
 491                                       d->gateway_value_addr[k],
 492                                       0, "gateway_value_addr"))
 493                        return -EINVAL;
 494        if (!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
 495                               "gateway_cmd_addr") ||
 496            !wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
 497                               "gateway_ctrl_address"))
 498                return -EINVAL;
 499
 500        wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n",
 501                   le32_to_cpu(d->gateway_addr_addr),
 502                   le32_to_cpu(d->gateway_cmd_addr),
 503                   le32_to_cpu(d->gateway_ctrl_address));
 504        wil_hex_dump_fw("val addresses: ", DUMP_PREFIX_NONE, 16, 4,
 505                        d->gateway_value_addr, sizeof(d->gateway_value_addr),
 506                        false);
 507
 508        for (i = 0; i < n; i++) {
 509                int rc;
 510                u32 a = le32_to_cpu(block[i].addr);
 511                u32 v[ARRAY_SIZE(block->value)];
 512
 513                for (k = 0; k < ARRAY_SIZE(block->value); k++)
 514                        v[k] = le32_to_cpu(block[i].value[k]);
 515
 516                wil_dbg_fw(wil, "  gw4 write[%3d] [0x%08x] <==\n", i, a);
 517                wil_hex_dump_fw("    val ", DUMP_PREFIX_NONE, 16, 4, v,
 518                                sizeof(v), false);
 519
 520                for (k = 0; k < ARRAY_SIZE(block->value); k++)
 521                        writel(v[k], gwa_val[k]);
 522                rc = gw_write(wil, gwa_addr, gwa_cmd, gwa_ctl, gw_cmd, a);
 523                if (rc)
 524                        return rc;
 525        }
 526
 527        return 0;
 528}
 529
 530static const struct {
 531        int type;
 532        int (*load_handler)(struct wil6210_priv *wil, const void *data,
 533                            size_t size);
 534        int (*parse_handler)(struct wil6210_priv *wil, const void *data,
 535                             size_t size);
 536} wil_fw_handlers[] = {
 537        {wil_fw_type_comment, fw_handle_comment, fw_handle_comment},
 538        {wil_fw_type_data, fw_handle_data, fw_ignore_section},
 539        {wil_fw_type_fill, fw_handle_fill, fw_ignore_section},
 540        /* wil_fw_type_action */
 541        /* wil_fw_type_verify */
 542        {wil_fw_type_file_header, fw_handle_file_header,
 543                fw_handle_file_header},
 544        {wil_fw_type_direct_write, fw_handle_direct_write, fw_ignore_section},
 545        {wil_fw_type_gateway_data, fw_handle_gateway_data, fw_ignore_section},
 546        {wil_fw_type_gateway_data4, fw_handle_gateway_data4,
 547                fw_ignore_section},
 548};
 549
 550static int wil_fw_handle_record(struct wil6210_priv *wil, int type,
 551                                const void *data, size_t size, bool load)
 552{
 553        int i;
 554
 555        for (i = 0; i < ARRAY_SIZE(wil_fw_handlers); i++)
 556                if (wil_fw_handlers[i].type == type)
 557                        return load ?
 558                                wil_fw_handlers[i].load_handler(
 559                                        wil, data, size) :
 560                                wil_fw_handlers[i].parse_handler(
 561                                        wil, data, size);
 562
 563        wil_err_fw(wil, "unknown record type: %d\n", type);
 564        return -EINVAL;
 565}
 566
 567/**
 568 * wil_fw_process - process section from FW file
 569 * if load is true: Load the FW and uCode code and data to the
 570 * corresponding device memory regions,
 571 * otherwise only parse and look for capabilities
 572 *
 573 * Return error code
 574 */
 575static int wil_fw_process(struct wil6210_priv *wil, const void *data,
 576                          size_t size, bool load)
 577{
 578        int rc = 0;
 579        const struct wil_fw_record_head *hdr;
 580        size_t s, hdr_sz;
 581
 582        for (hdr = data;; hdr = (const void *)hdr + s, size -= s) {
 583                if (size < sizeof(*hdr))
 584                        break;
 585                hdr_sz = le32_to_cpu(hdr->size);
 586                s = sizeof(*hdr) + hdr_sz;
 587                if (s > size)
 588                        break;
 589                if (hdr_sz % 4) {
 590                        wil_err_fw(wil, "unaligned record size: %zu\n",
 591                                   hdr_sz);
 592                        return -EINVAL;
 593                }
 594                rc = wil_fw_handle_record(wil, le16_to_cpu(hdr->type),
 595                                          &hdr[1], hdr_sz, load);
 596                if (rc)
 597                        return rc;
 598        }
 599        if (size) {
 600                wil_err_fw(wil, "unprocessed bytes: %zu\n", size);
 601                if (size >= sizeof(*hdr)) {
 602                        wil_err_fw(wil, "Stop at offset %ld"
 603                                   " record type %d [%zd bytes]\n",
 604                                   (long)((const void *)hdr - data),
 605                                   le16_to_cpu(hdr->type), hdr_sz);
 606                }
 607                return -EINVAL;
 608        }
 609
 610        return rc;
 611}
 612
 613/**
 614 * wil_request_firmware - Request firmware
 615 *
 616 * Request firmware image from the file
 617 * If load is true, load firmware to device, otherwise
 618 * only parse and extract capabilities
 619 *
 620 * Return error code
 621 */
 622int wil_request_firmware(struct wil6210_priv *wil, const char *name,
 623                         bool load)
 624{
 625        int rc, rc1;
 626        const struct firmware *fw;
 627        size_t sz;
 628        const void *d;
 629
 630        rc = request_firmware(&fw, name, wil_to_dev(wil));
 631        if (rc) {
 632                wil_err_fw(wil, "Failed to load firmware %s rc %d\n", name, rc);
 633                return rc;
 634        }
 635        wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size);
 636
 637        for (sz = fw->size, d = fw->data; sz; sz -= rc1, d += rc1) {
 638                rc1 = wil_fw_verify(wil, d, sz);
 639                if (rc1 < 0) {
 640                        rc = rc1;
 641                        goto out;
 642                }
 643                rc = wil_fw_process(wil, d, rc1, load);
 644                if (rc < 0)
 645                        goto out;
 646        }
 647
 648out:
 649        release_firmware(fw);
 650        return rc;
 651}
 652
 653/**
 654 * wil_brd_process - process section from BRD file
 655 *
 656 * Return error code
 657 */
 658static int wil_brd_process(struct wil6210_priv *wil, const void *data,
 659                           size_t size)
 660{
 661        int rc = 0;
 662        const struct wil_fw_record_head *hdr = data;
 663        size_t s, hdr_sz;
 664        u16 type;
 665
 666        /* Assuming the board file includes only one header record and one data
 667         * record. Each record starts with wil_fw_record_head.
 668         */
 669        if (size < sizeof(*hdr))
 670                return -EINVAL;
 671        s = sizeof(*hdr) + le32_to_cpu(hdr->size);
 672        if (s > size)
 673                return -EINVAL;
 674
 675        /* Skip the header record and handle the data record */
 676        hdr = (const void *)hdr + s;
 677        size -= s;
 678        if (size < sizeof(*hdr))
 679                return -EINVAL;
 680        hdr_sz = le32_to_cpu(hdr->size);
 681
 682        if (wil->brd_file_max_size && hdr_sz > wil->brd_file_max_size)
 683                return -EINVAL;
 684        if (sizeof(*hdr) + hdr_sz > size)
 685                return -EINVAL;
 686        if (hdr_sz % 4) {
 687                wil_err_fw(wil, "unaligned record size: %zu\n",
 688                           hdr_sz);
 689                return -EINVAL;
 690        }
 691        type = le16_to_cpu(hdr->type);
 692        if (type != wil_fw_type_data) {
 693                wil_err_fw(wil, "invalid record type for board file: %d\n",
 694                           type);
 695                return -EINVAL;
 696        }
 697        if (hdr_sz < sizeof(struct wil_fw_record_data)) {
 698                wil_err_fw(wil, "data record too short: %zu\n", hdr_sz);
 699                return -EINVAL;
 700        }
 701
 702        wil_dbg_fw(wil, "using addr from fw file: [0x%08x]\n",
 703                   wil->brd_file_addr);
 704
 705        rc = __fw_handle_data(wil, &hdr[1], hdr_sz,
 706                              cpu_to_le32(wil->brd_file_addr));
 707
 708        return rc;
 709}
 710
 711/**
 712 * wil_request_board - Request board file
 713 *
 714 * Request board image from the file
 715 * board file address and max size are read from FW file
 716 * during initialization.
 717 * brd file shall include one header and one data section.
 718 *
 719 * Return error code
 720 */
 721int wil_request_board(struct wil6210_priv *wil, const char *name)
 722{
 723        int rc, dlen;
 724        const struct firmware *brd;
 725
 726        rc = request_firmware(&brd, name, wil_to_dev(wil));
 727        if (rc) {
 728                wil_err_fw(wil, "Failed to load brd %s\n", name);
 729                return rc;
 730        }
 731        wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, brd->size);
 732
 733        /* Verify the header */
 734        dlen = wil_fw_verify(wil, brd->data, brd->size);
 735        if (dlen < 0) {
 736                rc = dlen;
 737                goto out;
 738        }
 739        /* Process the data record */
 740        rc = wil_brd_process(wil, brd->data, dlen);
 741
 742out:
 743        release_firmware(brd);
 744        return rc;
 745}
 746
 747/**
 748 * wil_fw_verify_file_exists - checks if firmware file exist
 749 *
 750 * @wil: driver context
 751 * @name: firmware file name
 752 *
 753 * return value - boolean, true for success, false for failure
 754 */
 755bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name)
 756{
 757        const struct firmware *fw;
 758        int rc;
 759
 760        rc = request_firmware(&fw, name, wil_to_dev(wil));
 761        if (!rc)
 762                release_firmware(fw);
 763        else
 764                wil_dbg_fw(wil, "<%s> not available: %d\n", name, rc);
 765        return !rc;
 766}
 767