.. | .. |
---|
| 1 | +// SPDX-License-Identifier: ISC |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2013 Broadcom Corporation |
---|
3 | | - * |
---|
4 | | - * Permission to use, copy, modify, and/or distribute this software for any |
---|
5 | | - * purpose with or without fee is hereby granted, provided that the above |
---|
6 | | - * copyright notice and this permission notice appear in all copies. |
---|
7 | | - * |
---|
8 | | - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
---|
9 | | - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
---|
10 | | - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
---|
11 | | - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
---|
12 | | - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
---|
13 | | - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
---|
14 | | - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
---|
15 | 4 | */ |
---|
16 | 5 | |
---|
| 6 | +#include <linux/efi.h> |
---|
17 | 7 | #include <linux/kernel.h> |
---|
18 | 8 | #include <linux/slab.h> |
---|
19 | 9 | #include <linux/device.h> |
---|
.. | .. |
---|
46 | 36 | * @state: current parser state. |
---|
47 | 37 | * @data: input buffer being parsed. |
---|
48 | 38 | * @nvram: output buffer with parse result. |
---|
49 | | - * @nvram_len: lenght of parse result. |
---|
| 39 | + * @nvram_len: length of parse result. |
---|
50 | 40 | * @line: current line. |
---|
51 | 41 | * @column: current column in line. |
---|
52 | 42 | * @pos: byte offset in input buffer. |
---|
.. | .. |
---|
69 | 59 | bool boardrev_found; |
---|
70 | 60 | }; |
---|
71 | 61 | |
---|
72 | | -/** |
---|
| 62 | +/* |
---|
73 | 63 | * is_nvram_char() - check if char is a valid one for NVRAM entry |
---|
74 | 64 | * |
---|
75 | 65 | * It accepts all printable ASCII chars except for '#' which opens a comment. |
---|
.. | .. |
---|
217 | 207 | size = BRCMF_FW_MAX_NVRAM_SIZE; |
---|
218 | 208 | else |
---|
219 | 209 | size = data_len; |
---|
| 210 | + /* Add space for properties we may add */ |
---|
| 211 | + size += strlen(BRCMF_FW_DEFAULT_BOARDREV) + 1; |
---|
220 | 212 | /* Alloc for extra 0 byte + roundup by 4 + length field */ |
---|
221 | 213 | size += 1 + 3 + sizeof(u32); |
---|
222 | 214 | nvp->nvram = kzalloc(size, GFP_KERNEL); |
---|
.. | .. |
---|
445 | 437 | |
---|
446 | 438 | static void brcmf_fw_request_done(const struct firmware *fw, void *ctx); |
---|
447 | 439 | |
---|
| 440 | +#ifdef CONFIG_EFI |
---|
| 441 | +/* In some cases the EFI-var stored nvram contains "ccode=ALL" or "ccode=XV" |
---|
| 442 | + * to specify "worldwide" compatible settings, but these 2 ccode-s do not work |
---|
| 443 | + * properly. "ccode=ALL" causes channels 12 and 13 to not be available, |
---|
| 444 | + * "ccode=XV" causes all 5GHz channels to not be available. So we replace both |
---|
| 445 | + * with "ccode=X2" which allows channels 12+13 and 5Ghz channels in |
---|
| 446 | + * no-Initiate-Radiation mode. This means that we will never send on these |
---|
| 447 | + * channels without first having received valid wifi traffic on the channel. |
---|
| 448 | + */ |
---|
| 449 | +static void brcmf_fw_fix_efi_nvram_ccode(char *data, unsigned long data_len) |
---|
| 450 | +{ |
---|
| 451 | + char *ccode; |
---|
| 452 | + |
---|
| 453 | + ccode = strnstr((char *)data, "ccode=ALL", data_len); |
---|
| 454 | + if (!ccode) |
---|
| 455 | + ccode = strnstr((char *)data, "ccode=XV\r", data_len); |
---|
| 456 | + if (!ccode) |
---|
| 457 | + return; |
---|
| 458 | + |
---|
| 459 | + ccode[6] = 'X'; |
---|
| 460 | + ccode[7] = '2'; |
---|
| 461 | + ccode[8] = '\r'; |
---|
| 462 | +} |
---|
| 463 | + |
---|
| 464 | +static u8 *brcmf_fw_nvram_from_efi(size_t *data_len_ret) |
---|
| 465 | +{ |
---|
| 466 | + const u16 name[] = { 'n', 'v', 'r', 'a', 'm', 0 }; |
---|
| 467 | + struct efivar_entry *nvram_efivar; |
---|
| 468 | + unsigned long data_len = 0; |
---|
| 469 | + u8 *data = NULL; |
---|
| 470 | + int err; |
---|
| 471 | + |
---|
| 472 | + nvram_efivar = kzalloc(sizeof(*nvram_efivar), GFP_KERNEL); |
---|
| 473 | + if (!nvram_efivar) |
---|
| 474 | + return NULL; |
---|
| 475 | + |
---|
| 476 | + memcpy(&nvram_efivar->var.VariableName, name, sizeof(name)); |
---|
| 477 | + nvram_efivar->var.VendorGuid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61, |
---|
| 478 | + 0xb5, 0x1f, 0x43, 0x26, |
---|
| 479 | + 0x81, 0x23, 0xd1, 0x13); |
---|
| 480 | + |
---|
| 481 | + err = efivar_entry_size(nvram_efivar, &data_len); |
---|
| 482 | + if (err) |
---|
| 483 | + goto fail; |
---|
| 484 | + |
---|
| 485 | + data = kmalloc(data_len, GFP_KERNEL); |
---|
| 486 | + if (!data) |
---|
| 487 | + goto fail; |
---|
| 488 | + |
---|
| 489 | + err = efivar_entry_get(nvram_efivar, NULL, &data_len, data); |
---|
| 490 | + if (err) |
---|
| 491 | + goto fail; |
---|
| 492 | + |
---|
| 493 | + brcmf_fw_fix_efi_nvram_ccode(data, data_len); |
---|
| 494 | + brcmf_info("Using nvram EFI variable\n"); |
---|
| 495 | + |
---|
| 496 | + kfree(nvram_efivar); |
---|
| 497 | + *data_len_ret = data_len; |
---|
| 498 | + return data; |
---|
| 499 | + |
---|
| 500 | +fail: |
---|
| 501 | + kfree(data); |
---|
| 502 | + kfree(nvram_efivar); |
---|
| 503 | + return NULL; |
---|
| 504 | +} |
---|
| 505 | +#else |
---|
| 506 | +static inline u8 *brcmf_fw_nvram_from_efi(size_t *data_len) { return NULL; } |
---|
| 507 | +#endif |
---|
| 508 | + |
---|
448 | 509 | static void brcmf_fw_free_request(struct brcmf_fw_request *req) |
---|
449 | 510 | { |
---|
450 | 511 | struct brcmf_fw_item *item; |
---|
.. | .. |
---|
463 | 524 | { |
---|
464 | 525 | struct brcmf_fw *fwctx = ctx; |
---|
465 | 526 | struct brcmf_fw_item *cur; |
---|
| 527 | + bool free_bcm47xx_nvram = false; |
---|
| 528 | + bool kfree_nvram = false; |
---|
466 | 529 | u32 nvram_length = 0; |
---|
467 | 530 | void *nvram = NULL; |
---|
468 | 531 | u8 *data = NULL; |
---|
469 | 532 | size_t data_len; |
---|
470 | | - bool raw_nvram; |
---|
471 | 533 | |
---|
472 | 534 | brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); |
---|
473 | 535 | |
---|
.. | .. |
---|
476 | 538 | if (fw && fw->data) { |
---|
477 | 539 | data = (u8 *)fw->data; |
---|
478 | 540 | data_len = fw->size; |
---|
479 | | - raw_nvram = false; |
---|
480 | 541 | } else { |
---|
481 | | - data = bcm47xx_nvram_get_contents(&data_len); |
---|
482 | | - if (!data && !(cur->flags & BRCMF_FW_REQF_OPTIONAL)) |
---|
| 542 | + if ((data = bcm47xx_nvram_get_contents(&data_len))) |
---|
| 543 | + free_bcm47xx_nvram = true; |
---|
| 544 | + else if ((data = brcmf_fw_nvram_from_efi(&data_len))) |
---|
| 545 | + kfree_nvram = true; |
---|
| 546 | + else if (!(cur->flags & BRCMF_FW_REQF_OPTIONAL)) |
---|
483 | 547 | goto fail; |
---|
484 | | - raw_nvram = true; |
---|
485 | 548 | } |
---|
486 | 549 | |
---|
487 | 550 | if (data) |
---|
.. | .. |
---|
489 | 552 | fwctx->req->domain_nr, |
---|
490 | 553 | fwctx->req->bus_nr); |
---|
491 | 554 | |
---|
492 | | - if (raw_nvram) |
---|
| 555 | + if (free_bcm47xx_nvram) |
---|
493 | 556 | bcm47xx_nvram_release_contents(data); |
---|
| 557 | + if (kfree_nvram) |
---|
| 558 | + kfree(data); |
---|
| 559 | + |
---|
494 | 560 | release_firmware(fw); |
---|
495 | 561 | if (!nvram && !(cur->flags & BRCMF_FW_REQF_OPTIONAL)) |
---|
496 | 562 | goto fail; |
---|
.. | .. |
---|
504 | 570 | return -ENOENT; |
---|
505 | 571 | } |
---|
506 | 572 | |
---|
507 | | -static int brcmf_fw_request_next_item(struct brcmf_fw *fwctx, bool async) |
---|
| 573 | +static int brcmf_fw_complete_request(const struct firmware *fw, |
---|
| 574 | + struct brcmf_fw *fwctx) |
---|
508 | 575 | { |
---|
509 | | - struct brcmf_fw_item *cur; |
---|
510 | | - const struct firmware *fw = NULL; |
---|
511 | | - int ret; |
---|
512 | | - |
---|
513 | | - cur = &fwctx->req->items[fwctx->curpos]; |
---|
514 | | - |
---|
515 | | - brcmf_dbg(TRACE, "%srequest for %s\n", async ? "async " : "", |
---|
516 | | - cur->path); |
---|
517 | | - |
---|
518 | | - if (async) |
---|
519 | | - ret = request_firmware_nowait(THIS_MODULE, true, cur->path, |
---|
520 | | - fwctx->dev, GFP_KERNEL, fwctx, |
---|
521 | | - brcmf_fw_request_done); |
---|
522 | | - else |
---|
523 | | - ret = request_firmware(&fw, cur->path, fwctx->dev); |
---|
524 | | - |
---|
525 | | - if (ret < 0) { |
---|
526 | | - brcmf_fw_request_done(NULL, fwctx); |
---|
527 | | - } else if (!async && fw) { |
---|
528 | | - brcmf_dbg(TRACE, "firmware %s %sfound\n", cur->path, |
---|
529 | | - fw ? "" : "not "); |
---|
530 | | - if (cur->type == BRCMF_FW_TYPE_BINARY) |
---|
531 | | - cur->binary = fw; |
---|
532 | | - else if (cur->type == BRCMF_FW_TYPE_NVRAM) |
---|
533 | | - brcmf_fw_request_nvram_done(fw, fwctx); |
---|
534 | | - else |
---|
535 | | - release_firmware(fw); |
---|
536 | | - |
---|
537 | | - return -EAGAIN; |
---|
538 | | - } |
---|
539 | | - return 0; |
---|
540 | | -} |
---|
541 | | - |
---|
542 | | -static void brcmf_fw_request_done(const struct firmware *fw, void *ctx) |
---|
543 | | -{ |
---|
544 | | - struct brcmf_fw *fwctx = ctx; |
---|
545 | | - struct brcmf_fw_item *cur; |
---|
| 576 | + struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos]; |
---|
546 | 577 | int ret = 0; |
---|
547 | 578 | |
---|
548 | | - cur = &fwctx->req->items[fwctx->curpos]; |
---|
549 | | - |
---|
550 | | - brcmf_dbg(TRACE, "enter: firmware %s %sfound\n", cur->path, |
---|
551 | | - fw ? "" : "not "); |
---|
552 | | - |
---|
553 | | - if (!fw) |
---|
554 | | - ret = -ENOENT; |
---|
| 579 | + brcmf_dbg(TRACE, "firmware %s %sfound\n", cur->path, fw ? "" : "not "); |
---|
555 | 580 | |
---|
556 | 581 | switch (cur->type) { |
---|
557 | 582 | case BRCMF_FW_TYPE_NVRAM: |
---|
558 | 583 | ret = brcmf_fw_request_nvram_done(fw, fwctx); |
---|
559 | 584 | break; |
---|
560 | 585 | case BRCMF_FW_TYPE_BINARY: |
---|
561 | | - cur->binary = fw; |
---|
| 586 | + if (fw) |
---|
| 587 | + cur->binary = fw; |
---|
| 588 | + else |
---|
| 589 | + ret = -ENOENT; |
---|
562 | 590 | break; |
---|
563 | 591 | default: |
---|
564 | 592 | /* something fishy here so bail out early */ |
---|
565 | 593 | brcmf_err("unknown fw type: %d\n", cur->type); |
---|
566 | 594 | release_firmware(fw); |
---|
567 | 595 | ret = -EINVAL; |
---|
568 | | - goto fail; |
---|
569 | 596 | } |
---|
570 | 597 | |
---|
571 | | - if (ret < 0 && !(cur->flags & BRCMF_FW_REQF_OPTIONAL)) |
---|
572 | | - goto fail; |
---|
| 598 | + return (cur->flags & BRCMF_FW_REQF_OPTIONAL) ? 0 : ret; |
---|
| 599 | +} |
---|
573 | 600 | |
---|
574 | | - do { |
---|
575 | | - if (++fwctx->curpos == fwctx->req->n_items) { |
---|
576 | | - ret = 0; |
---|
577 | | - goto done; |
---|
578 | | - } |
---|
| 601 | +static int brcmf_fw_request_firmware(const struct firmware **fw, |
---|
| 602 | + struct brcmf_fw *fwctx) |
---|
| 603 | +{ |
---|
| 604 | + struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos]; |
---|
| 605 | + int ret; |
---|
579 | 606 | |
---|
580 | | - ret = brcmf_fw_request_next_item(fwctx, false); |
---|
581 | | - } while (ret == -EAGAIN); |
---|
| 607 | + /* nvram files are board-specific, first try a board-specific path */ |
---|
| 608 | + if (cur->type == BRCMF_FW_TYPE_NVRAM && fwctx->req->board_type) { |
---|
| 609 | + char alt_path[BRCMF_FW_NAME_LEN]; |
---|
582 | 610 | |
---|
583 | | - return; |
---|
| 611 | + strlcpy(alt_path, cur->path, BRCMF_FW_NAME_LEN); |
---|
| 612 | + /* strip .txt at the end */ |
---|
| 613 | + alt_path[strlen(alt_path) - 4] = 0; |
---|
| 614 | + strlcat(alt_path, ".", BRCMF_FW_NAME_LEN); |
---|
| 615 | + strlcat(alt_path, fwctx->req->board_type, BRCMF_FW_NAME_LEN); |
---|
| 616 | + strlcat(alt_path, ".txt", BRCMF_FW_NAME_LEN); |
---|
584 | 617 | |
---|
585 | | -fail: |
---|
586 | | - brcmf_dbg(TRACE, "failed err=%d: dev=%s, fw=%s\n", ret, |
---|
587 | | - dev_name(fwctx->dev), cur->path); |
---|
588 | | - brcmf_fw_free_request(fwctx->req); |
---|
589 | | - fwctx->req = NULL; |
---|
590 | | -done: |
---|
| 618 | + ret = request_firmware(fw, alt_path, fwctx->dev); |
---|
| 619 | + if (ret == 0) |
---|
| 620 | + return ret; |
---|
| 621 | + } |
---|
| 622 | + |
---|
| 623 | + return request_firmware(fw, cur->path, fwctx->dev); |
---|
| 624 | +} |
---|
| 625 | + |
---|
| 626 | +static void brcmf_fw_request_done(const struct firmware *fw, void *ctx) |
---|
| 627 | +{ |
---|
| 628 | + struct brcmf_fw *fwctx = ctx; |
---|
| 629 | + int ret; |
---|
| 630 | + |
---|
| 631 | + ret = brcmf_fw_complete_request(fw, fwctx); |
---|
| 632 | + |
---|
| 633 | + while (ret == 0 && ++fwctx->curpos < fwctx->req->n_items) { |
---|
| 634 | + brcmf_fw_request_firmware(&fw, fwctx); |
---|
| 635 | + ret = brcmf_fw_complete_request(fw, ctx); |
---|
| 636 | + } |
---|
| 637 | + |
---|
| 638 | + if (ret) { |
---|
| 639 | + brcmf_fw_free_request(fwctx->req); |
---|
| 640 | + fwctx->req = NULL; |
---|
| 641 | + } |
---|
591 | 642 | fwctx->done(fwctx->dev, ret, fwctx->req); |
---|
592 | 643 | kfree(fwctx); |
---|
593 | 644 | } |
---|
.. | .. |
---|
611 | 662 | void (*fw_cb)(struct device *dev, int err, |
---|
612 | 663 | struct brcmf_fw_request *req)) |
---|
613 | 664 | { |
---|
| 665 | + struct brcmf_fw_item *first = &req->items[0]; |
---|
614 | 666 | struct brcmf_fw *fwctx; |
---|
| 667 | + int ret; |
---|
615 | 668 | |
---|
616 | 669 | brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev)); |
---|
617 | 670 | if (!fw_cb) |
---|
.. | .. |
---|
628 | 681 | fwctx->req = req; |
---|
629 | 682 | fwctx->done = fw_cb; |
---|
630 | 683 | |
---|
631 | | - brcmf_fw_request_next_item(fwctx, true); |
---|
| 684 | + ret = request_firmware_nowait(THIS_MODULE, true, first->path, |
---|
| 685 | + fwctx->dev, GFP_KERNEL, fwctx, |
---|
| 686 | + brcmf_fw_request_done); |
---|
| 687 | + if (ret < 0) |
---|
| 688 | + brcmf_fw_request_done(NULL, fwctx); |
---|
| 689 | + |
---|
632 | 690 | return 0; |
---|
633 | 691 | } |
---|
634 | 692 | |
---|
.. | .. |
---|
644 | 702 | size_t mp_path_len; |
---|
645 | 703 | u32 i, j; |
---|
646 | 704 | char end = '\0'; |
---|
647 | | - size_t reqsz; |
---|
| 705 | + |
---|
| 706 | + if (chiprev >= BITS_PER_TYPE(u32)) { |
---|
| 707 | + brcmf_err("Invalid chip revision %u\n", chiprev); |
---|
| 708 | + return NULL; |
---|
| 709 | + } |
---|
648 | 710 | |
---|
649 | 711 | for (i = 0; i < table_size; i++) { |
---|
650 | 712 | if (mapping_table[i].chipid == chip && |
---|
.. | .. |
---|
652 | 714 | break; |
---|
653 | 715 | } |
---|
654 | 716 | |
---|
| 717 | + brcmf_chip_name(chip, chiprev, chipname, sizeof(chipname)); |
---|
| 718 | + |
---|
655 | 719 | if (i == table_size) { |
---|
656 | | - brcmf_err("Unknown chipid %d [%d]\n", chip, chiprev); |
---|
| 720 | + brcmf_err("Unknown chip %s\n", chipname); |
---|
657 | 721 | return NULL; |
---|
658 | 722 | } |
---|
659 | 723 | |
---|
660 | | - reqsz = sizeof(*fwreq) + n_fwnames * sizeof(struct brcmf_fw_item); |
---|
661 | | - fwreq = kzalloc(reqsz, GFP_KERNEL); |
---|
| 724 | + fwreq = kzalloc(struct_size(fwreq, items, n_fwnames), GFP_KERNEL); |
---|
662 | 725 | if (!fwreq) |
---|
663 | 726 | return NULL; |
---|
664 | | - |
---|
665 | | - brcmf_chip_name(chip, chiprev, chipname, sizeof(chipname)); |
---|
666 | 727 | |
---|
667 | 728 | brcmf_info("using %s for chip %s\n", |
---|
668 | 729 | mapping_table[i].fw_base, chipname); |
---|
.. | .. |
---|
676 | 737 | |
---|
677 | 738 | for (j = 0; j < n_fwnames; j++) { |
---|
678 | 739 | fwreq->items[j].path = fwnames[j].path; |
---|
| 740 | + fwnames[j].path[0] = '\0'; |
---|
679 | 741 | /* check if firmware path is provided by module parameter */ |
---|
680 | 742 | if (brcmf_mp_global.firmware_path[0] != '\0') { |
---|
681 | 743 | strlcpy(fwnames[j].path, mp_path, |
---|