// SPDX-License-Identifier: GPL-2.0-or-later/* * * Bluetooth support for Intel devices * * Copyright (C) 2015 Intel Corporation */ #include <linux/module.h>#include <linux/firmware.h>#include <linux/regmap.h>#include <asm/unaligned.h> #include <net/bluetooth/bluetooth.h>#include <net/bluetooth/hci_core.h> #include "btintel.h" #define VERSION "0.1" #define BDADDR_INTEL (&(bdaddr_t){{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})#define RSA_HEADER_LEN 644#define CSS_HEADER_OFFSET 8#define ECDSA_OFFSET 644#define ECDSA_HEADER_LEN 320 #define CMD_WRITE_BOOT_PARAMS 0xfc0estruct cmd_write_boot_params { u32 boot_addr; u8 fw_build_num; u8 fw_build_ww; u8 fw_build_yy;} __packed; int btintel_check_bdaddr(struct hci_dev *hdev){ struct hci_rp_read_bd_addr *bda; struct sk_buff *skb; skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); bt_dev_err(hdev, "Reading Intel device address failed (%d)", err); return err; } if (skb->len != sizeof(*bda)) { bt_dev_err(hdev, "Intel device address length mismatch"); kfree_skb(skb); return -EIO; } bda = (struct hci_rp_read_bd_addr *)skb->data; /* For some Intel based controllers, the default Bluetooth device * address 00:03:19:9E:8B:00 can be found. These controllers are * fully operational, but have the danger of duplicate addresses * and that in turn can cause problems with Bluetooth operation. */ if (!bacmp(&bda->bdaddr, BDADDR_INTEL)) { bt_dev_err(hdev, "Found Intel default device address (%pMR)", &bda->bdaddr); set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); } kfree_skb(skb); return 0;}EXPORT_SYMBOL_GPL(btintel_check_bdaddr); int btintel_enter_mfg(struct hci_dev *hdev){ static const u8 param[] = { 0x01, 0x00 }; struct sk_buff *skb; skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Entering manufacturer mode failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); } kfree_skb(skb); return 0;}EXPORT_SYMBOL_GPL(btintel_enter_mfg); int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched){ u8 param[] = { 0x00, 0x00 }; struct sk_buff *skb; /* The 2nd command parameter specifies the manufacturing exit method: * 0x00: Just disable the manufacturing mode (0x00). * 0x01: Disable manufacturing mode and reset with patches deactivated. * 0x02: Disable manufacturing mode and reset with patches activated. */ if (reset) param[1] |= patched ? 0x02 : 0x01; skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Exiting manufacturer mode failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); } kfree_skb(skb); return 0;}EXPORT_SYMBOL_GPL(btintel_exit_mfg); int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr){ struct sk_buff *skb; int err; skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); bt_dev_err(hdev, "Changing Intel device address failed (%d)", err); return err; } kfree_skb(skb); return 0;}EXPORT_SYMBOL_GPL(btintel_set_bdaddr); int btintel_set_diag(struct hci_dev *hdev, bool enable){ struct sk_buff *skb; u8 param[3]; int err; if (enable) { param[0] = 0x03; param[1] = 0x03; param[2] = 0x03; } else { param[0] = 0x00; param[1] = 0x00; param[2] = 0x00; } skb = __hci_cmd_sync(hdev, 0xfc43, 3, param, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); if (err == -ENODATA) goto done; bt_dev_err(hdev, "Changing Intel diagnostic mode failed (%d)", err); return err; } kfree_skb(skb); done: btintel_set_event_mask(hdev, enable); return 0;}EXPORT_SYMBOL_GPL(btintel_set_diag); int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable){ int err, ret; err = btintel_enter_mfg(hdev); if (err) return err; ret = btintel_set_diag(hdev, enable); err = btintel_exit_mfg(hdev, false, false); if (err) return err; return ret;}EXPORT_SYMBOL_GPL(btintel_set_diag_mfg); void btintel_hw_error(struct hci_dev *hdev, u8 code){ struct sk_buff *skb; u8 type = 0x00; bt_dev_err(hdev, "Hardware error 0x%2.2x", code); skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reset after hardware error failed (%ld)", PTR_ERR(skb)); return; } kfree_skb(skb); skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Retrieving Intel exception info failed (%ld)", PTR_ERR(skb)); return; } if (skb->len != 13) { bt_dev_err(hdev, "Exception info size mismatch"); kfree_skb(skb); return; } bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1)); kfree_skb(skb);}EXPORT_SYMBOL_GPL(btintel_hw_error); int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver){ const char *variant; /* The hardware platform number has a fixed value of 0x37 and * for now only accept this single value. */ if (ver->hw_platform != 0x37) { bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)", ver->hw_platform); return -EINVAL; } /* Check for supported iBT hardware variants of this firmware * loading method. * * This check has been put in place to ensure correct forward * compatibility options when newer hardware variants come along. */ switch (ver->hw_variant) { case 0x0b: /* SfP */ case 0x0c: /* WsP */ case 0x11: /* JfP */ case 0x12: /* ThP */ case 0x13: /* HrP */ case 0x14: /* CcP */ break; default: bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)", ver->hw_variant); return -EINVAL; } switch (ver->fw_variant) { case 0x06: variant = "Bootloader"; break; case 0x23: variant = "Firmware"; break; default: bt_dev_err(hdev, "Unsupported firmware variant(%02x)", ver->fw_variant); return -EINVAL; } bt_dev_info(hdev, "%s revision %u.%u build %u week %u %u", variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f, ver->fw_build_num, ver->fw_build_ww, 2000 + ver->fw_build_yy); return 0;}EXPORT_SYMBOL_GPL(btintel_version_info); int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen, const void *param){ while (plen > 0) { struct sk_buff *skb; u8 cmd_param[253], fragment_len = (plen > 252) ? 252 : plen; cmd_param[0] = fragment_type; memcpy(cmd_param + 1, param, fragment_len); skb = __hci_cmd_sync(hdev, 0xfc09, fragment_len + 1, cmd_param, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) return PTR_ERR(skb); kfree_skb(skb); plen -= fragment_len; param += fragment_len; } return 0;}EXPORT_SYMBOL_GPL(btintel_secure_send); int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name){ const struct firmware *fw; struct sk_buff *skb; const u8 *fw_ptr; int err; err = request_firmware_direct(&fw, ddc_name, &hdev->dev); if (err < 0) { bt_dev_err(hdev, "Failed to load Intel DDC file %s (%d)", ddc_name, err); return err; } bt_dev_info(hdev, "Found Intel DDC parameters: %s", ddc_name); fw_ptr = fw->data; /* DDC file contains one or more DDC structure which has * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2). */ while (fw->size > fw_ptr - fw->data) { u8 cmd_plen = fw_ptr[0] + sizeof(u8); skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Failed to send Intel_Write_DDC (%ld)", PTR_ERR(skb)); release_firmware(fw); return PTR_ERR(skb); } fw_ptr += cmd_plen; kfree_skb(skb); } release_firmware(fw); bt_dev_info(hdev, "Applying Intel DDC parameters completed"); return 0;}EXPORT_SYMBOL_GPL(btintel_load_ddc_config); int btintel_set_event_mask(struct hci_dev *hdev, bool debug){ u8 mask[8] = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; struct sk_buff *skb; int err; if (debug) mask[1] |= 0x62; skb = __hci_cmd_sync(hdev, 0xfc52, 8, mask, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); bt_dev_err(hdev, "Setting Intel event mask failed (%d)", err); return err; } kfree_skb(skb); return 0;}EXPORT_SYMBOL_GPL(btintel_set_event_mask); int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug){ int err, ret; err = btintel_enter_mfg(hdev); if (err) return err; ret = btintel_set_event_mask(hdev, debug); err = btintel_exit_mfg(hdev, false, false); if (err) return err; return ret;}EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg); int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver){ struct sk_buff *skb; skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading Intel version information failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); } if (skb->len != sizeof(*ver)) { bt_dev_err(hdev, "Intel version event size mismatch"); kfree_skb(skb); return -EILSEQ; } memcpy(ver, skb->data, sizeof(*ver)); kfree_skb(skb); return 0;}EXPORT_SYMBOL_GPL(btintel_read_version); int btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version){ const char *variant; /* The hardware platform number has a fixed value of 0x37 and * for now only accept this single value. */ if (INTEL_HW_PLATFORM(version->cnvi_bt) != 0x37) { bt_dev_err(hdev, "Unsupported Intel hardware platform (0x%2x)", INTEL_HW_PLATFORM(version->cnvi_bt)); return -EINVAL; } /* Check for supported iBT hardware variants of this firmware * loading method. * * This check has been put in place to ensure correct forward * compatibility options when newer hardware variants come along. */ switch (INTEL_HW_VARIANT(version->cnvi_bt)) { case 0x17: /* TyP */ case 0x18: /* Slr */ case 0x19: /* Slr-F */ break; default: bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)", INTEL_HW_VARIANT(version->cnvi_bt)); return -EINVAL; } switch (version->img_type) { case 0x01: variant = "Bootloader"; /* It is required that every single firmware fragment is acknowledged * with a command complete event. If the boot parameters indicate * that this bootloader does not send them, then abort the setup. */ if (version->limited_cce != 0x00) { bt_dev_err(hdev, "Unsupported Intel firmware loading method (0x%x)", version->limited_cce); return -EINVAL; } /* Secure boot engine type should be either 1 (ECDSA) or 0 (RSA) */ if (version->sbe_type > 0x01) { bt_dev_err(hdev, "Unsupported Intel secure boot engine type (0x%x)", version->sbe_type); return -EINVAL; } bt_dev_info(hdev, "Device revision is %u", version->dev_rev_id); bt_dev_info(hdev, "Secure boot is %s", version->secure_boot ? "enabled" : "disabled"); bt_dev_info(hdev, "OTP lock is %s", version->otp_lock ? "enabled" : "disabled"); bt_dev_info(hdev, "API lock is %s", version->api_lock ? "enabled" : "disabled"); bt_dev_info(hdev, "Debug lock is %s", version->debug_lock ? "enabled" : "disabled"); bt_dev_info(hdev, "Minimum firmware build %u week %u %u", version->min_fw_build_nn, version->min_fw_build_cw, 2000 + version->min_fw_build_yy); break; case 0x03: variant = "Firmware"; break; default: bt_dev_err(hdev, "Unsupported image type(%02x)", version->img_type); return -EINVAL; } bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant, 2000 + (version->timestamp >> 8), version->timestamp & 0xff, version->build_type, version->build_num); return 0;}EXPORT_SYMBOL_GPL(btintel_version_info_tlv); int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *version){ struct sk_buff *skb; const u8 param[1] = { 0xFF }; if (!version) return -EINVAL; skb = __hci_cmd_sync(hdev, 0xfc05, 1, param, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading Intel version information failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); } Lines 1–500 of 1282. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * * Bluetooth support for Intel devices * * Copyright (C) 2015 Intel Corporation */ #include <linux/module.h> #include <linux/firmware.h> #include <linux/regmap.h> #include <asm/unaligned.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> #include "btintel.h" #define VERSION "0.1" #define BDADDR_INTEL (&(bdaddr_t){{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}}) #define RSA_HEADER_LEN 644 #define CSS_HEADER_OFFSET 8 #define ECDSA_OFFSET 644 #define ECDSA_HEADER_LEN 320 #define CMD_WRITE_BOOT_PARAMS 0xfc0e struct cmd_write_boot_params { u32 boot_addr; u8 fw_build_num; u8 fw_build_ww; u8 fw_build_yy; } __packed; int btintel_check_bdaddr(struct hci_dev *hdev) { struct hci_rp_read_bd_addr *bda; struct sk_buff *skb; skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); bt_dev_err(hdev, "Reading Intel device address failed (%d)", err); return err; } if (skb->len != sizeof(*bda)) { bt_dev_err(hdev, "Intel device address length mismatch"); kfree_skb(skb); return -EIO; } bda = (struct hci_rp_read_bd_addr *)skb->data; /* For some Intel based controllers, the default Bluetooth device * address 00:03:19:9E:8B:00 can be found. These controllers are * fully operational, but have the danger of duplicate addresses * and that in turn can cause problems with Bluetooth operation. */ if (!bacmp(&bda->bdaddr, BDADDR_INTEL)) { bt_dev_err(hdev, "Found Intel default device address (%pMR)", &bda->bdaddr); set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); } kfree_skb(skb); return 0; } EXPORT_SYMBOL_GPL(btintel_check_bdaddr); int btintel_enter_mfg(struct hci_dev *hdev) { static const u8 param[] = { 0x01, 0x00 }; struct sk_buff *skb; skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Entering manufacturer mode failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); } kfree_skb(skb); return 0; } EXPORT_SYMBOL_GPL(btintel_enter_mfg); int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched) { u8 param[] = { 0x00, 0x00 }; struct sk_buff *skb; /* The 2nd command parameter specifies the manufacturing exit method: * 0x00: Just disable the manufacturing mode (0x00). * 0x01: Disable manufacturing mode and reset with patches deactivated. * 0x02: Disable manufacturing mode and reset with patches activated. */ if (reset) param[1] |= patched ? 0x02 : 0x01; skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Exiting manufacturer mode failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); } kfree_skb(skb); return 0; } EXPORT_SYMBOL_GPL(btintel_exit_mfg); int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) { struct sk_buff *skb; int err; skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); bt_dev_err(hdev, "Changing Intel device address failed (%d)", err); return err; } kfree_skb(skb); return 0; } EXPORT_SYMBOL_GPL(btintel_set_bdaddr); int btintel_set_diag(struct hci_dev *hdev, bool enable) { struct sk_buff *skb; u8 param[3]; int err; if (enable) { param[0] = 0x03; param[1] = 0x03; param[2] = 0x03; } else { param[0] = 0x00; param[1] = 0x00; param[2] = 0x00; } skb = __hci_cmd_sync(hdev, 0xfc43, 3, param, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); if (err == -ENODATA) goto done; bt_dev_err(hdev, "Changing Intel diagnostic mode failed (%d)", err); return err; } kfree_skb(skb); done: btintel_set_event_mask(hdev, enable); return 0; } EXPORT_SYMBOL_GPL(btintel_set_diag); int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable) { int err, ret; err = btintel_enter_mfg(hdev); if (err) return err; ret = btintel_set_diag(hdev, enable); err = btintel_exit_mfg(hdev, false, false); if (err) return err; return ret; } EXPORT_SYMBOL_GPL(btintel_set_diag_mfg); void btintel_hw_error(struct hci_dev *hdev, u8 code) { struct sk_buff *skb; u8 type = 0x00; bt_dev_err(hdev, "Hardware error 0x%2.2x", code); skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reset after hardware error failed (%ld)", PTR_ERR(skb)); return; } kfree_skb(skb); skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Retrieving Intel exception info failed (%ld)", PTR_ERR(skb)); return; } if (skb->len != 13) { bt_dev_err(hdev, "Exception info size mismatch"); kfree_skb(skb); return; } bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1)); kfree_skb(skb); } EXPORT_SYMBOL_GPL(btintel_hw_error); int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver) { const char *variant; /* The hardware platform number has a fixed value of 0x37 and * for now only accept this single value. */ if (ver->hw_platform != 0x37) { bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)", ver->hw_platform); return -EINVAL; } /* Check for supported iBT hardware variants of this firmware * loading method. * * This check has been put in place to ensure correct forward * compatibility options when newer hardware variants come along. */ switch (ver->hw_variant) { case 0x0b: /* SfP */ case 0x0c: /* WsP */ case 0x11: /* JfP */ case 0x12: /* ThP */ case 0x13: /* HrP */ case 0x14: /* CcP */ break; default: bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)", ver->hw_variant); return -EINVAL; } switch (ver->fw_variant) { case 0x06: variant = "Bootloader"; break; case 0x23: variant = "Firmware"; break; default: bt_dev_err(hdev, "Unsupported firmware variant(%02x)", ver->fw_variant); return -EINVAL; } bt_dev_info(hdev, "%s revision %u.%u build %u week %u %u", variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f, ver->fw_build_num, ver->fw_build_ww, 2000 + ver->fw_build_yy); return 0; } EXPORT_SYMBOL_GPL(btintel_version_info); int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen, const void *param) { while (plen > 0) { struct sk_buff *skb; u8 cmd_param[253], fragment_len = (plen > 252) ? 252 : plen; cmd_param[0] = fragment_type; memcpy(cmd_param + 1, param, fragment_len); skb = __hci_cmd_sync(hdev, 0xfc09, fragment_len + 1, cmd_param, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) return PTR_ERR(skb); kfree_skb(skb); plen -= fragment_len; param += fragment_len; } return 0; } EXPORT_SYMBOL_GPL(btintel_secure_send); int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name) { const struct firmware *fw; struct sk_buff *skb; const u8 *fw_ptr; int err; err = request_firmware_direct(&fw, ddc_name, &hdev->dev); if (err < 0) { bt_dev_err(hdev, "Failed to load Intel DDC file %s (%d)", ddc_name, err); return err; } bt_dev_info(hdev, "Found Intel DDC parameters: %s", ddc_name); fw_ptr = fw->data; /* DDC file contains one or more DDC structure which has * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2). */ while (fw->size > fw_ptr - fw->data) { u8 cmd_plen = fw_ptr[0] + sizeof(u8); skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Failed to send Intel_Write_DDC (%ld)", PTR_ERR(skb)); release_firmware(fw); return PTR_ERR(skb); } fw_ptr += cmd_plen; kfree_skb(skb); } release_firmware(fw); bt_dev_info(hdev, "Applying Intel DDC parameters completed"); return 0; } EXPORT_SYMBOL_GPL(btintel_load_ddc_config); int btintel_set_event_mask(struct hci_dev *hdev, bool debug) { u8 mask[8] = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; struct sk_buff *skb; int err; if (debug) mask[1] |= 0x62; skb = __hci_cmd_sync(hdev, 0xfc52, 8, mask, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); bt_dev_err(hdev, "Setting Intel event mask failed (%d)", err); return err; } kfree_skb(skb); return 0; } EXPORT_SYMBOL_GPL(btintel_set_event_mask); int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug) { int err, ret; err = btintel_enter_mfg(hdev); if (err) return err; ret = btintel_set_event_mask(hdev, debug); err = btintel_exit_mfg(hdev, false, false); if (err) return err; return ret; } EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg); int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver) { struct sk_buff *skb; skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading Intel version information failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); } if (skb->len != sizeof(*ver)) { bt_dev_err(hdev, "Intel version event size mismatch"); kfree_skb(skb); return -EILSEQ; } memcpy(ver, skb->data, sizeof(*ver)); kfree_skb(skb); return 0; } EXPORT_SYMBOL_GPL(btintel_read_version); int btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version) { const char *variant; /* The hardware platform number has a fixed value of 0x37 and * for now only accept this single value. */ if (INTEL_HW_PLATFORM(version->cnvi_bt) != 0x37) { bt_dev_err(hdev, "Unsupported Intel hardware platform (0x%2x)", INTEL_HW_PLATFORM(version->cnvi_bt)); return -EINVAL; } /* Check for supported iBT hardware variants of this firmware * loading method. * * This check has been put in place to ensure correct forward * compatibility options when newer hardware variants come along. */ switch (INTEL_HW_VARIANT(version->cnvi_bt)) { case 0x17: /* TyP */ case 0x18: /* Slr */ case 0x19: /* Slr-F */ break; default: bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)", INTEL_HW_VARIANT(version->cnvi_bt)); return -EINVAL; } switch (version->img_type) { case 0x01: variant = "Bootloader"; /* It is required that every single firmware fragment is acknowledged * with a command complete event. If the boot parameters indicate * that this bootloader does not send them, then abort the setup. */ if (version->limited_cce != 0x00) { bt_dev_err(hdev, "Unsupported Intel firmware loading method (0x%x)", version->limited_cce); return -EINVAL; } /* Secure boot engine type should be either 1 (ECDSA) or 0 (RSA) */ if (version->sbe_type > 0x01) { bt_dev_err(hdev, "Unsupported Intel secure boot engine type (0x%x)", version->sbe_type); return -EINVAL; } bt_dev_info(hdev, "Device revision is %u", version->dev_rev_id); bt_dev_info(hdev, "Secure boot is %s", version->secure_boot ? "enabled" : "disabled"); bt_dev_info(hdev, "OTP lock is %s", version->otp_lock ? "enabled" : "disabled"); bt_dev_info(hdev, "API lock is %s", version->api_lock ? "enabled" : "disabled"); bt_dev_info(hdev, "Debug lock is %s", version->debug_lock ? "enabled" : "disabled"); bt_dev_info(hdev, "Minimum firmware build %u week %u %u", version->min_fw_build_nn, version->min_fw_build_cw, 2000 + version->min_fw_build_yy); break; case 0x03: variant = "Firmware"; break; default: bt_dev_err(hdev, "Unsupported image type(%02x)", version->img_type); return -EINVAL; } bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant, 2000 + (version->timestamp >> 8), version->timestamp & 0xff, version->build_type, version->build_num); return 0; } EXPORT_SYMBOL_GPL(btintel_version_info_tlv); int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *version) { struct sk_buff *skb; const u8 param[1] = { 0xFF }; if (!version) return -EINVAL; skb = __hci_cmd_sync(hdev, 0xfc05, 1, param, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading Intel version information failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); }