|
24 | 24 | #include <linux/module.h>
|
25 | 25 | #include <linux/firmware.h>
|
26 | 26 | #include <linux/regmap.h>
|
| 27 | +#include <asm/unaligned.h> |
27 | 28 |
|
28 | 29 | #include <net/bluetooth/bluetooth.h>
|
29 | 30 | #include <net/bluetooth/hci_core.h>
|
@@ -569,6 +570,160 @@ struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
|
569 | 570 | }
|
570 | 571 | EXPORT_SYMBOL_GPL(btintel_regmap_init);
|
571 | 572 |
|
| 573 | +int btintel_send_intel_reset(struct hci_dev *hdev, u32 boot_param) |
| 574 | +{ |
| 575 | + struct intel_reset params = { 0x00, 0x01, 0x00, 0x01, 0x00000000 }; |
| 576 | + struct sk_buff *skb; |
| 577 | + |
| 578 | + params.boot_param = cpu_to_le32(boot_param); |
| 579 | + |
| 580 | + skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(params), ¶ms, |
| 581 | + HCI_INIT_TIMEOUT); |
| 582 | + if (IS_ERR(skb)) { |
| 583 | + bt_dev_err(hdev, "Failed to send Intel Reset command"); |
| 584 | + return PTR_ERR(skb); |
| 585 | + } |
| 586 | + |
| 587 | + kfree_skb(skb); |
| 588 | + |
| 589 | + return 0; |
| 590 | +} |
| 591 | +EXPORT_SYMBOL_GPL(btintel_send_intel_reset); |
| 592 | + |
| 593 | +int btintel_read_boot_params(struct hci_dev *hdev, |
| 594 | + struct intel_boot_params *params) |
| 595 | +{ |
| 596 | + struct sk_buff *skb; |
| 597 | + |
| 598 | + skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT); |
| 599 | + if (IS_ERR(skb)) { |
| 600 | + bt_dev_err(hdev, "Reading Intel boot parameters failed (%ld)", |
| 601 | + PTR_ERR(skb)); |
| 602 | + return PTR_ERR(skb); |
| 603 | + } |
| 604 | + |
| 605 | + if (skb->len != sizeof(*params)) { |
| 606 | + bt_dev_err(hdev, "Intel boot parameters size mismatch"); |
| 607 | + kfree_skb(skb); |
| 608 | + return -EILSEQ; |
| 609 | + } |
| 610 | + |
| 611 | + memcpy(params, skb->data, sizeof(*params)); |
| 612 | + |
| 613 | + kfree_skb(skb); |
| 614 | + |
| 615 | + if (params->status) { |
| 616 | + bt_dev_err(hdev, "Intel boot parameters command failed (%02x)", |
| 617 | + params->status); |
| 618 | + return -bt_to_errno(params->status); |
| 619 | + } |
| 620 | + |
| 621 | + bt_dev_info(hdev, "Device revision is %u", |
| 622 | + le16_to_cpu(params->dev_revid)); |
| 623 | + |
| 624 | + bt_dev_info(hdev, "Secure boot is %s", |
| 625 | + params->secure_boot ? "enabled" : "disabled"); |
| 626 | + |
| 627 | + bt_dev_info(hdev, "OTP lock is %s", |
| 628 | + params->otp_lock ? "enabled" : "disabled"); |
| 629 | + |
| 630 | + bt_dev_info(hdev, "API lock is %s", |
| 631 | + params->api_lock ? "enabled" : "disabled"); |
| 632 | + |
| 633 | + bt_dev_info(hdev, "Debug lock is %s", |
| 634 | + params->debug_lock ? "enabled" : "disabled"); |
| 635 | + |
| 636 | + bt_dev_info(hdev, "Minimum firmware build %u week %u %u", |
| 637 | + params->min_fw_build_nn, params->min_fw_build_cw, |
| 638 | + 2000 + params->min_fw_build_yy); |
| 639 | + |
| 640 | + return 0; |
| 641 | +} |
| 642 | +EXPORT_SYMBOL_GPL(btintel_read_boot_params); |
| 643 | + |
| 644 | +int btintel_download_firmware(struct hci_dev *hdev, const struct firmware *fw, |
| 645 | + u32 *boot_param) |
| 646 | +{ |
| 647 | + int err; |
| 648 | + const u8 *fw_ptr; |
| 649 | + u32 frag_len; |
| 650 | + |
| 651 | + /* Start the firmware download transaction with the Init fragment |
| 652 | + * represented by the 128 bytes of CSS header. |
| 653 | + */ |
| 654 | + err = btintel_secure_send(hdev, 0x00, 128, fw->data); |
| 655 | + if (err < 0) { |
| 656 | + bt_dev_err(hdev, "Failed to send firmware header (%d)", err); |
| 657 | + goto done; |
| 658 | + } |
| 659 | + |
| 660 | + /* Send the 256 bytes of public key information from the firmware |
| 661 | + * as the PKey fragment. |
| 662 | + */ |
| 663 | + err = btintel_secure_send(hdev, 0x03, 256, fw->data + 128); |
| 664 | + if (err < 0) { |
| 665 | + bt_dev_err(hdev, "Failed to send firmware pkey (%d)", err); |
| 666 | + goto done; |
| 667 | + } |
| 668 | + |
| 669 | + /* Send the 256 bytes of signature information from the firmware |
| 670 | + * as the Sign fragment. |
| 671 | + */ |
| 672 | + err = btintel_secure_send(hdev, 0x02, 256, fw->data + 388); |
| 673 | + if (err < 0) { |
| 674 | + bt_dev_err(hdev, "Failed to send firmware signature (%d)", err); |
| 675 | + goto done; |
| 676 | + } |
| 677 | + |
| 678 | + fw_ptr = fw->data + 644; |
| 679 | + frag_len = 0; |
| 680 | + |
| 681 | + while (fw_ptr - fw->data < fw->size) { |
| 682 | + struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len); |
| 683 | + |
| 684 | + /* Each SKU has a different reset parameter to use in the |
| 685 | + * HCI_Intel_Reset command and it is embedded in the firmware |
| 686 | + * data. So, instead of using static value per SKU, check |
| 687 | + * the firmware data and save it for later use. |
| 688 | + */ |
| 689 | + if (le16_to_cpu(cmd->opcode) == 0xfc0e) { |
| 690 | + /* The boot parameter is the first 32-bit value |
| 691 | + * and rest of 3 octets are reserved. |
| 692 | + */ |
| 693 | + *boot_param = get_unaligned_le32(fw_ptr + sizeof(*cmd)); |
| 694 | + |
| 695 | + bt_dev_dbg(hdev, "boot_param=0x%x", *boot_param); |
| 696 | + } |
| 697 | + |
| 698 | + frag_len += sizeof(*cmd) + cmd->plen; |
| 699 | + |
| 700 | + /* The parameter length of the secure send command requires |
| 701 | + * a 4 byte alignment. It happens so that the firmware file |
| 702 | + * contains proper Intel_NOP commands to align the fragments |
| 703 | + * as needed. |
| 704 | + * |
| 705 | + * Send set of commands with 4 byte alignment from the |
| 706 | + * firmware data buffer as a single Data fragement. |
| 707 | + */ |
| 708 | + if (!(frag_len % 4)) { |
| 709 | + err = btintel_secure_send(hdev, 0x01, frag_len, fw_ptr); |
| 710 | + if (err < 0) { |
| 711 | + bt_dev_err(hdev, |
| 712 | + "Failed to send firmware data (%d)", |
| 713 | + err); |
| 714 | + goto done; |
| 715 | + } |
| 716 | + |
| 717 | + fw_ptr += frag_len; |
| 718 | + frag_len = 0; |
| 719 | + } |
| 720 | + } |
| 721 | + |
| 722 | +done: |
| 723 | + return err; |
| 724 | +} |
| 725 | +EXPORT_SYMBOL_GPL(btintel_download_firmware); |
| 726 | + |
572 | 727 | MODULE_AUTHOR( "Marcel Holtmann <[email protected]>");
|
573 | 728 | MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
|
574 | 729 | MODULE_VERSION(VERSION);
|
|
0 commit comments