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