r.sbin/bluetooth/iwmbtfw/iwmbt_fw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h index 2666d123c8f0..eb6909a1f91d 100644 --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h @@ -52,6 +52,10 @@ struct iwmbt_version { uint8_t fw_patch_num; } __attribute__ ((packed)); +/* Known values for fw_variant */ +#define FW_VARIANT_BOOTLOADER 0x06 /* Bootloader mode */ +#define FW_VARIANT_OPERATIONAL 0x23 /* Operational mode */ + struct iwmbt_boot_params { uint8_t status; uint8_t otp_format; @@ -131,6 +135,10 @@ struct iwmbt_version_tlv { bdaddr_t otp_bd_addr; }; +/* Known TLV img_type values */ +#define TLV_IMG_TYPE_BOOTLOADER 0x01 /* Bootloader mode */ +#define TLV_IMG_TYPE_OPERATIONAL 0x03 /* Operational mode */ + struct iwmbt_firmware { char *fwname; int len; diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c index 1efd24ecf9f6..255181b8f4bc 100644 --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c @@ -437,7 +437,8 @@ iwmbt_enter_manufacturer(struct libusb_device_handle *hdl) } int -iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode) +iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, + enum iwmbt_mm_exit mode) { int ret, transferred; static struct iwmbt_hci_cmd cmd = { @@ -447,17 +448,7 @@ iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode) }; uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; - /* - * The mode sets the type of reset we want to perform: - * 0x00: simply exit manufacturer mode without a reset. - * 0x01: exit manufacturer mode with a reset and patches disabled - * 0x02: exit manufacturer mode with a reset and patches enabled - */ - if (mode > 2) { - iwmbt_debug("iwmbt_exit_manufacturer(): unknown mode (%d)", - mode); - } - cmd.data[1] = mode; + cmd.data[1] = (uint8_t)mode; ret = iwmbt_hci_command(hdl, &cmd, diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h index 89ee344fe587..aac885dfd153 100644 --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h @@ -58,6 +58,18 @@ struct iwmbt_hci_event_cmd_compl { uint8_t data[]; } __attribute__ ((packed)); +/* + * Manufacturer mode exit type: selects reset type, + * 0x00: simply exit manufacturer mode without a reset. + * 0x01: exit manufacturer mode with a reset and patches disabled + * 0x02: exit manufacturer mode with a reset and patches enabled + */ +enum iwmbt_mm_exit { + IWMBT_MM_EXIT_ONLY = 0x00, + IWMBT_MM_EXIT_COLD_RESET = 0x01, + IWMBT_MM_EXIT_WARM_RESET = 0x02, +}; + #define IWMBT_HCI_EVT_COMPL_SIZE(payload) \ (offsetof(struct iwmbt_hci_event_cmd_compl, data) + sizeof(payload)) #define IWMBT_HCI_EVENT_COMPL_HEAD_SIZE \ @@ -91,7 +103,7 @@ extern int iwmbt_load_fwfile(struct libusb_device_handle *hdl, const struct iwmbt_firmware *fw, uint32_t *boot_param, int offset); extern int iwmbt_enter_manufacturer(struct libusb_device_handle *hdl); extern int iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, - int mode); + enum iwmbt_mm_exit mode); extern int iwmbt_get_version(struct libusb_device_handle *hdl, struct iwmbt_version *version); extern int iwmbt_get_version_tlv(struct libusb_device_handle *hdl, diff --git a/usr.sbin/bluetooth/iwmbtfw/main.c b/usr.sbin/bluetooth/iwmbtfw/main.c index e4bb22af0e8a..497edcb254cf 100644 --- a/usr.sbin/bluetooth/iwmbtfw/main.c +++ b/usr.sbin/bluetooth/iwmbtfw/main.c @@ -215,9 +215,11 @@ iwmbt_dump_version_tlv(struct iwmbt_version_tlv *ver) ver->otp_bd_addr.b[2], ver->otp_bd_addr.b[1], ver->otp_bd_addr.b[0]); - if (ver->img_type == 0x01 || ver->img_type == 0x03) + if (ver->img_type == TLV_IMG_TYPE_BOOTLOADER || + ver->img_type == TLV_IMG_TYPE_OPERATIONAL) iwmbt_info("%s timestamp %u.%u buildtype %u build %u", - ver->img_type == 0x01 ? "Bootloader" : "Firmware", + (ver->img_type == TLV_IMG_TYPE_BOOTLOADER ? + "Bootloader" : "Firmware"), 2000 + (ver->timestamp >> 8), ver->timestamp & 0xff, ver->build_type, @@ -399,22 +401,266 @@ usage(void) exit(127); } + +/* + * Returns 0 on success. + */ +static int +handle_7260(libusb_device_handle *hdl, char *firmware_dir) +{ + int r; + struct iwmbt_version ver; + char *firmware_path = NULL; + + r = iwmbt_get_version(hdl, &ver); + if (r < 0) { + iwmbt_debug("iwmbt_get_version() failed code %d", r); + return 1; + } + iwmbt_dump_version(&ver); + iwmbt_debug("fw_patch_num=0x%02x", (int) ver.fw_patch_num); + + /* fw_patch_num = >0 operational mode */ + if (ver.fw_patch_num > 0x00) { + iwmbt_info("Firmware has already been downloaded"); + return 0; + } + + firmware_path = iwmbt_get_fwname(&ver, NULL, firmware_dir, "bseq"); + if (firmware_path == NULL) + return 1; + iwmbt_debug("firmware_path = %s", firmware_path); + + r = iwmbt_enter_manufacturer(hdl); + if (r < 0) { + iwmbt_debug("iwmbt_enter_manufacturer() failed code %d", r); + return 1; + } + + /* Download firmware */ + r = iwmbt_patch_firmware(hdl, firmware_path); + free(firmware_path); + if (r < 0) { + (void)iwmbt_exit_manufacturer(hdl, IWMBT_MM_EXIT_COLD_RESET); + return 1; + } + + iwmbt_info("Firmware download complete"); + + r = iwmbt_exit_manufacturer(hdl, + (r == 0 ? IWMBT_MM_EXIT_ONLY : IWMBT_MM_EXIT_WARM_RESET)); + if (r < 0) { + iwmbt_debug("iwmbt_exit_manufacturer() failed code %d", r); + return 1; + } + + /* Once device is running in operational mode we can ignore failures */ + + /* Dump actual controller version */ + r = iwmbt_get_version(hdl, &ver); + if (r == 0) + iwmbt_dump_version(&ver); + + if (iwmbt_enter_manufacturer(hdl) < 0) + return 0; + r = iwmbt_set_event_mask(hdl); + if (r == 0) + iwmbt_info("Intel Event Mask is set"); + (void)iwmbt_exit_manufacturer(hdl, IWMBT_MM_EXIT_ONLY); + + return 0; +} + + +/* + * Returns 0 on success. + */ +static int +handle_8260(libusb_device_handle *hdl, char *firmware_dir) +{ + int r; + uint32_t boot_param; + struct iwmbt_version ver; + struct iwmbt_boot_params params; + char *firmware_path = NULL; + + r = iwmbt_get_version(hdl, &ver); + if (r < 0) { + iwmbt_debug("iwmbt_get_version() failed code %d", r); + return 1; + } + iwmbt_dump_version(&ver); + iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant); + + if (ver.fw_variant == FW_VARIANT_OPERATIONAL) { + iwmbt_info("Firmware has already been downloaded"); + return 0; + } + + if (ver.fw_variant != FW_VARIANT_BOOTLOADER){ + iwmbt_err("unknown fw_variant 0x%02x", (int) ver.fw_variant); + return 1; + } + + /* Read Intel Secure Boot Params */ + r = iwmbt_get_boot_params(hdl, ¶ms); + if (r < 0) { + iwmbt_debug("iwmbt_get_boot_params() failed!"); + return 1; + } + iwmbt_dump_boot_params(¶ms); + + /* Check if firmware fragments are ACKed with a cmd complete event */ + if (params.limited_cce != 0x00) { + iwmbt_err("Unsupported Intel firmware loading method (%u)", + params.limited_cce); + return 1; + } + + firmware_path = iwmbt_get_fwname(&ver, ¶ms, firmware_dir, "sfi"); + if (firmware_path == NULL) + return 1; + iwmbt_debug("firmware_path = %s", firmware_path); + + /* Download firmware and parse it for magic Intel Reset parameter */ + r = iwmbt_init_firmware(hdl, firmware_path, &boot_param, 0, 0); + free(firmware_path); + if (r < 0) + return 1; + + iwmbt_info("Firmware download complete"); + + r = iwmbt_intel_reset(hdl, boot_param); + if (r < 0) { + iwmbt_debug("iwmbt_intel_reset() failed!"); + return 1; + } + + iwmbt_info("Firmware operational"); + + /* Once device is running in operational mode we can ignore failures */ + + /* Dump actual controller version */ + r = iwmbt_get_version(hdl, &ver); + if (r == 0) + iwmbt_dump_version(&ver); + + /* Apply the device configuration (DDC) parameters */ + firmware_path = iwmbt_get_fwname(&ver, ¶ms, firmware_dir, "ddc"); + iwmbt_debug("ddc_path = %s", firmware_path); + if (firmware_path != NULL) { + r = iwmbt_init_ddc(hdl, firmware_path); + if (r == 0) + iwmbt_info("DDC download complete"); + free(firmware_path); + } + + r = iwmbt_set_event_mask(hdl); + if (r == 0) + iwmbt_info("Intel Event Mask is set"); + + return 0; +} + + +static int +handle_9260(libusb_device_handle *hdl, char *firmware_dir) +{ + int r; + uint32_t boot_param; + struct iwmbt_version vl; + struct iwmbt_version_tlv vt; + char *firmware_path = NULL; + + r = iwmbt_get_version_tlv(hdl, &vt); + if (r < 0) { + iwmbt_debug("iwmbt_get_version_tlv() failed code %d", r); + return 1; + } + iwmbt_dump_version_tlv(&vt); + iwmbt_debug("img_type=0x%02x", (int) vt.img_type); + + if (vt.img_type == TLV_IMG_TYPE_OPERATIONAL) { + iwmbt_info("Firmware has already been downloaded"); + return 0; + } + + if (vt.img_type != TLV_IMG_TYPE_BOOTLOADER) { + iwmbt_err("unknown img_type 0x%02x", (int) vt.img_type); + return 1; + } + + /* Check if firmware fragments are ACKed with a cmd complete event */ + if (vt.limited_cce != 0x00) { + iwmbt_err("Unsupported Intel firmware loading method (%u)", + vt.limited_cce); + return 1; + } + + /* Check if secure boot engine is supported: 1 (ECDSA) or 0 (RSA) */ + if (vt.sbe_type > 0x01) { + iwmbt_err("Unsupported secure boot engine (%u)", + vt.sbe_type); + return 1; + } + + firmware_path = iwmbt_get_fwname_tlv(&vt, firmware_dir, "sfi"); + if (firmware_path == NULL) + return 1; + iwmbt_debug("firmware_path = %s", firmware_path); + + /* Download firmware and parse it for magic Intel Reset parameter */ + r = iwmbt_init_firmware(hdl, firmware_path, &boot_param, + vt.cnvi_bt >> 16 & 0x3f, vt.sbe_type); + free(firmware_path); + if (r < 0) + return 1; + + iwmbt_info("Firmware download complete"); + + r = iwmbt_intel_reset(hdl, boot_param); + if (r < 0) { + iwmbt_debug("iwmbt_intel_reset() failed!"); + return 1; + } + + iwmbt_info("Firmware operational"); + + /* Once device is running in operational mode we can ignore failures */ + + r = iwmbt_get_version(hdl, &vl); + if (r == 0) + iwmbt_dump_version(&vl); + + /* Apply the device configuration (DDC) parameters */ + firmware_path = iwmbt_get_fwname_tlv(&vt, firmware_dir, "ddc"); + iwmbt_debug("ddc_path = %s", firmware_path); + if (firmware_path != NULL) { + r = iwmbt_init_ddc(hdl, firmware_path); + if (r == 0) + iwmbt_info("DDC download complete"); + free(firmware_path); + } + + r = iwmbt_set_event_mask(hdl); + if (r == 0) + iwmbt_info("Intel Event Mask is set"); + + return 0; +} + + int main(int argc, char *argv[]) { libusb_context *ctx = NULL; libusb_device *dev = NULL; libusb_device_handle *hdl = NULL; - static struct iwmbt_version ver; - static struct iwmbt_version_tlv ver_tlv; - static struct iwmbt_boot_params params; - uint32_t boot_param; int r; uint8_t bus_id = 0, dev_id = 0; int devid_set = 0; int n; char *firmware_dir = NULL; - char *firmware_path = NULL; int retcode = 1; enum iwmbt_device iwmbt_device; @@ -494,256 +740,30 @@ main(int argc, char *argv[]) goto shutdown; } - if (iwmbt_device == IWMBT_DEVICE_7260) { - - /* Get Intel version */ - r = iwmbt_get_version(hdl, &ver); - if (r < 0) { - iwmbt_debug("iwmbt_get_version() failed code %d", r); - goto shutdown; - } - iwmbt_dump_version(&ver); - iwmbt_debug("fw_patch_num=0x%02x", (int) ver.fw_patch_num); - - /* fw_patch_num = >0 operational mode */ - if (ver.fw_patch_num > 0x00) { - iwmbt_info("Firmware has already been downloaded"); - retcode = 0; - goto reset; - } - - firmware_path = iwmbt_get_fwname(&ver, ¶ms, firmware_dir, "bseq"); - if (firmware_path == NULL) - goto shutdown; - - iwmbt_debug("firmware_path = %s", firmware_path); - - /* Check firmware file exists before changing HW mode */ - r = access(firmware_path, R_OK); - if (r) { - perror("Failed to open firmware"); - goto shutdown; - } - - /* Enter manufacturer mode */ - r = iwmbt_enter_manufacturer(hdl); - if (r < 0) { - iwmbt_debug("iwmbt_enter_manufacturer() failed code %d", r); - goto shutdown; - } - - /* Download firmware and parse it for magic Intel Reset parameter */ - r = iwmbt_patch_firmware(hdl, firmware_path); - free(firmware_path); - if (r < 0) { - (void)iwmbt_exit_manufacturer(hdl, 0x01); - goto shutdown; - } - - iwmbt_info("Firmware download complete"); - - /* Exit manufacturer mode */ - r = iwmbt_exit_manufacturer(hdl, r == 0 ? 0x00 : 0x02); - if (r < 0) { - iwmbt_debug("iwmbt_exit_manufacturer() failed code %d", r); - goto shutdown; - } - - /* Once device is running in operational mode we can ignore failures */ - retcode = 0; - - /* Execute Read Intel Version one more time */ - r = iwmbt_get_version(hdl, &ver); - if (r == 0) - iwmbt_dump_version(&ver); - - /* Set Intel Event mask */ - if (iwmbt_enter_manufacturer(hdl) < 0) - goto reset; - r = iwmbt_set_event_mask(hdl); - if (r == 0) - iwmbt_info("Intel Event Mask is set"); - (void)iwmbt_exit_manufacturer(hdl, 0x00); - - } else if (iwmbt_device == IWMBT_DEVICE_8260) { - - /* Get Intel version */ - r = iwmbt_get_version(hdl, &ver); - if (r < 0) { - iwmbt_debug("iwmbt_get_version() failed code %d", r); - goto shutdown; - } - iwmbt_dump_version(&ver); - iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant); - - /* fw_variant = 0x06 bootloader mode / 0x23 operational mode */ - if (ver.fw_variant == 0x23) { - iwmbt_info("Firmware has already been downloaded"); - retcode = 0; - goto reset; - } - - if (ver.fw_variant != 0x06){ - iwmbt_err("unknown fw_variant 0x%02x", (int) ver.fw_variant); - goto shutdown; - } - - /* Read Intel Secure Boot Params */ - r = iwmbt_get_boot_params(hdl, ¶ms); - if (r < 0) { - iwmbt_debug("iwmbt_get_boot_params() failed!"); - goto shutdown; - } - iwmbt_dump_boot_params(¶ms); - - /* Check if firmware fragments are ACKed with a cmd complete event */ - if (params.limited_cce != 0x00) { - iwmbt_err("Unsupported Intel firmware loading method (%u)", - params.limited_cce); - goto shutdown; - } - - firmware_path = iwmbt_get_fwname(&ver, ¶ms, firmware_dir, "sfi"); - if (firmware_path == NULL) - goto shutdown; - - iwmbt_debug("firmware_path = %s", firmware_path); - - /* Download firmware and parse it for magic Intel Reset parameter */ - r = iwmbt_init_firmware(hdl, firmware_path, &boot_param, 0, 0); - free(firmware_path); - if (r < 0) - goto shutdown; - - iwmbt_info("Firmware download complete"); - - r = iwmbt_intel_reset(hdl, boot_param); - if (r < 0) { - iwmbt_debug("iwmbt_intel_reset() failed!"); - goto shutdown; - } - - iwmbt_info("Firmware operational"); - - /* Once device is running in operational mode we can ignore failures */ - retcode = 0; - - /* Execute Read Intel Version one more time */ - r = iwmbt_get_version(hdl, &ver); - if (r == 0) - iwmbt_dump_version(&ver); - - /* Apply the device configuration (DDC) parameters */ - firmware_path = iwmbt_get_fwname(&ver, ¶ms, firmware_dir, "ddc"); - iwmbt_debug("ddc_path = %s", firmware_path); - if (firmware_path != NULL) { - r = iwmbt_init_ddc(hdl, firmware_path); - if (r == 0) - iwmbt_info("DDC download complete"); - free(firmware_path); - } - - /* Set Intel Event mask */ - r = iwmbt_set_event_mask(hdl); - if (r == 0) - iwmbt_info("Intel Event Mask is set"); - - } else { - - /* Get Intel version */ - r = iwmbt_get_version_tlv(hdl, &ver_tlv); - if (r < 0) { - iwmbt_debug("iwmbt_get_version_tlv() failed code %d", r); - goto shutdown; - } - iwmbt_dump_version_tlv(&ver_tlv); - iwmbt_debug("img_type=0x%02x", (int) ver_tlv.img_type); - - /* img_type = 0x01 bootloader mode / 0x03 operational mode */ - if (ver_tlv.img_type == 0x03) { - iwmbt_info("Firmware has already been downloaded"); - retcode = 0; - goto reset; - } - - if (ver_tlv.img_type != 0x01){ - iwmbt_err("unknown img_type 0x%02x", (int) ver_tlv.img_type); - goto shutdown; - } - - /* Check if firmware fragments are ACKed with a cmd complete event */ - if (ver_tlv.limited_cce != 0x00) { - iwmbt_err("Unsupported Intel firmware loading method (%u)", - ver_tlv.limited_cce); - goto shutdown; - } - - /* Check if secure boot engine is supported: 1 (ECDSA) or 0 (RSA) */ - if (ver_tlv.sbe_type > 0x01) { - iwmbt_err("Unsupported secure boot engine (%u)", - ver_tlv.sbe_type); - goto shutdown; - } - - firmware_path = iwmbt_get_fwname_tlv(&ver_tlv, firmware_dir, "sfi"); - if (firmware_path == NULL) - goto shutdown; - - iwmbt_debug("firmware_path = %s", firmware_path); - - /* Download firmware and parse it for magic Intel Reset parameter */ - r = iwmbt_init_firmware(hdl, firmware_path, &boot_param, - ver_tlv.cnvi_bt >> 16 & 0x3f, ver_tlv.sbe_type); - free(firmware_path); - if (r < 0) - goto shutdown; - - r = iwmbt_intel_reset(hdl, boot_param); - if (r < 0) { - iwmbt_debug("iwmbt_intel_reset() failed!"); - goto shutdown; - } - - iwmbt_info("Firmware operational"); - - /* Once device is running in operational mode we can ignore failures */ - retcode = 0; - - /* Execute Read Intel Version one more time */ - r = iwmbt_get_version(hdl, &ver); - if (r == 0) - iwmbt_dump_version(&ver); - - /* Apply the device configuration (DDC) parameters */ - firmware_path = iwmbt_get_fwname_tlv(&ver_tlv, firmware_dir, "ddc"); - iwmbt_debug("ddc_path = %s", firmware_path); - if (firmware_path != NULL) { - r = iwmbt_init_ddc(hdl, firmware_path); - if (r == 0) - iwmbt_info("DDC download complete"); - free(firmware_path); - } - - /* Set Intel Event mask */ - r = iwmbt_set_event_mask(hdl); - if (r == 0) - iwmbt_info("Intel Event Mask is set"); - - iwmbt_info("Firmware download complete"); + switch(iwmbt_device) { + case IWMBT_DEVICE_7260: + retcode = handle_7260(hdl, firmware_dir); + break; + case IWMBT_DEVICE_8260: + retcode = handle_8260(hdl, firmware_dir); + break; + case IWMBT_DEVICE_9260: + retcode = handle_9260(hdl, firmware_dir); + break; + default: + iwmbt_err("FIXME: unknown iwmbt type %d", (int)iwmbt_device); + retcode = 1; } -reset: - - /* Ask kernel driver to probe and attach device again */ - r = libusb_reset_device(hdl); - if (r != 0) - iwmbt_err("libusb_reset_device() failed: %s", - libusb_strerror(r)); + if (retcode == 0) { + /* Ask kernel driver to probe and attach device again */ + r = libusb_reset_device(hdl); + if (r != 0) + iwmbt_err("libusb_reset_device() failed: %s", + libusb_strerror(r)); + } shutdown: - - /* Shutdown */ - if (hdl != NULL) libusb_close(hdl);