From 8174b66a45d11902a458d5cf6417c05e03e29e00 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 21 Sep 2018 09:19:23 +0200 Subject: [PATCH] chipet/intel: support for firmware upload and configuration of Intel Wireless 8260 and 8265 --- README.md | 1 + chipset/README.md | 5 + chipset/intel/.gitignore | 2 + chipset/intel/Makefile.inc | 28 ++ .../intel/btstack_chipset_intel_firmware.c | 417 ++++++++++++++++++ .../intel/btstack_chipset_intel_firmware.h | 65 +++ 6 files changed, 518 insertions(+) create mode 100644 chipset/intel/.gitignore create mode 100644 chipset/intel/Makefile.inc create mode 100644 chipset/intel/btstack_chipset_intel_firmware.c create mode 100644 chipset/intel/btstack_chipset_intel_firmware.h diff --git a/README.md b/README.md index 66c99a6bf..82776e68d 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ Cypress CYW20704 | Dual mode | H4, H5, USB | Probably | Dialog Semiconductor DA14581, DA14585 | LE | H4, SPI | n.a. | da14581 | Official HCI firmware used Espressif ESP32 | Dual mode | VHCI | Not yet | | SoC with Bluetooth and Wifi EM 9301, 9304 | LE | SPI | n.a. | em9301 | Custom HCI SPI implementation +Intel Dual Wireless 8260, 8265 | Dual mode | USB | Probably | intel | Nordic nRF | LE | H4 | n.a. | | Requires custom HCI firmware STM STLC2500D | Classic | H4 | No (didn't try) | stlc2500d | Custom deep sleep management not supported Toshiba TC35661 | Dual mode | H4 | No | tc3566 | diff --git a/chipset/README.md b/chipset/README.md index b42286b6f..7dd56d8b2 100644 --- a/chipset/README.md +++ b/chipset/README.md @@ -64,6 +64,7 @@ Dialog DA14585 | LE | H4, SPI | No | n.a. Espressif ESP32 | Dual mode | VHCI | Yes | Not yet | Yes | Yes | | SoC with Bluetooth and Wifi EM 9301 | LE | SPI, H4 | No | n.a. | No | No | em9301 | Custom HCI SPI implementation EM 9304 | LE | SPI, H4 | Yes | n.a. | Yes | Yes | em9301 | Custom HCI SPI implementation +Intel Dual Wireless 8260, 8265 | Dual mode | USB | Yes | Probably | Don't know | Don't know | intel | Nordic nRF | LE | H4 | Fixed Random | n.a. | Yes | Yes | | Requires HCI firmware STM STLC2500D | Classic | H4 | No | No (didn't try) | n.a | n.a. | stlc2500d | Custom deep sleep management not supported Toshiba TC35661 | Dual mode | H4 | No | No | No | No | tc3566 | Only -007/009 models provide full HCI. See below @@ -173,6 +174,10 @@ EM9304 is used by the 'stm32-l053r8-em9304' port in BTstack. The port.c file als **BTstack integration**: The common code for the EM9304 is provided by *btstack_chipset_em9301.c*. During the setup, *btstack_chipset_em9301_instance* function is used to get a *btstack_chipset_t* instance and passed to *hci_init* function. It enables to set the BD Addr during start. +## Intel Dual Wireless 8260, 8265 + +Wifi/Bluetooth combo cards mainly used in mobile computers. The Bluetooth part requires the upload of a firmware file and a configuration file. SCO, DLE, Multiple roles not tested. + ## Nordic nRF5 series The Single-Mode LE chipsets from the Nordic nRF5 series chipsets usually do not have an HCI interface. Instead, they provide an LE Bluetooth Stack as a binary library, the so-called *SoftDevices*. Developer can write their Bluetooth application on top of this library. Since the chipset can be programmed, it can also be loaded with a firmware that provides a regular HCI H4 interface for a Host. diff --git a/chipset/intel/.gitignore b/chipset/intel/.gitignore new file mode 100644 index 000000000..759946d3e --- /dev/null +++ b/chipset/intel/.gitignore @@ -0,0 +1,2 @@ +*.ddc +*.sfi diff --git a/chipset/intel/Makefile.inc b/chipset/intel/Makefile.inc new file mode 100644 index 000000000..7f9f3162c --- /dev/null +++ b/chipset/intel/Makefile.inc @@ -0,0 +1,28 @@ +# +# Makefile to download firmware and config files for Intel Wireless chipsets from Linux firmware tree +# +# Supported: +# - Intel Wireless 8260 +# - Intel Wireless 8265 + +LINUX_FIRMWARE_URL = https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tree/ + +FILES = \ + ibt-11-5.ddc \ + ibt-11-5.sfi \ + ibt-12-16.ddc \ + ibt-12-16.sfi + +all: $(FILES) + +%.sfi: + @echo "Downloading %@" + @curl -O -L $(LINUX_FIRMWARE_URL)/intel/$@ + +%.ddc: + @echo "Downloading %@" + @curl -O -L $(LINUX_FIRMWARE_URL)/intel/$@ + +clean: + rm -f $(FILES) + diff --git a/chipset/intel/btstack_chipset_intel_firmware.c b/chipset/intel/btstack_chipset_intel_firmware.c new file mode 100644 index 000000000..05f0399c6 --- /dev/null +++ b/chipset/intel/btstack_chipset_intel_firmware.c @@ -0,0 +1,417 @@ +#define __BTSTACK_FILE__ "btstack_chipset_intel_firmware.c" + +#include +#include +#include + +#include "btstack_chipset_intel_firmware.h" +#include "hci_cmd.h" +#include "bluetooth.h" +#include "hci_dump.h" +#include "btstack_event.h" +#include "btstack_debug.h" +#include "btstack_util.h" +#include "btstack_run_loop.h" + +// Vendor specific structs + +typedef struct { + uint8_t status; + uint8_t hw_platform; + uint8_t hw_variant; + uint8_t hw_revision; + uint8_t fw_variant; + uint8_t fw_revision; + uint8_t fw_build_num; + uint8_t fw_build_ww; + uint8_t fw_build_yy; + uint8_t fw_patch_num; +} intel_version_t; + +typedef struct { + uint8_t status; + uint8_t otp_format; + uint8_t otp_content; + uint8_t otp_patch; + uint16_t dev_revid; + uint8_t secure_boot; + uint8_t key_from_hdr; + uint8_t key_type; + uint8_t otp_lock; + uint8_t api_lock; + uint8_t debug_lock; + bd_addr_t otp_bdaddr; + uint8_t min_fw_build_nn; + uint8_t min_fw_build_cw; + uint8_t min_fw_build_yy; + uint8_t limited_cce; + uint8_t unlocked_state; +} intel_boot_params_t; + +// Vendor sepcific commands + +static const hci_cmd_t hci_intel_read_version = { + 0xfc05, "" +}; +static const hci_cmd_t hci_intel_read_secure_boot_params = { + 0xfc0d, "" +}; + +static const hci_cmd_t hci_intel_reset_param = { + 0xfc01, "11111111" +}; + +static const hci_cmd_t hci_intel_set_event_mask = { + 0xfc52, "11111111" +}; + +static const hci_cmd_t hci_intel_fc9f = { + 0xfc9f, "1" +}; + +// state + +const hci_transport_t * transport; + +static int state = 0; + +static uint8_t hci_outgoing[300]; +static uint8_t fw_buffer[300]; + +static uint8_t hw_variant; +static uint16_t dev_revid; + +static FILE * fw_file; +static uint32_t fw_offset; + +static void (*done)(int result); + +// functions + +static int transport_send_packet(uint8_t packet_type, const uint8_t * packet, uint16_t size){ + hci_dump_packet(HCI_COMMAND_DATA_PACKET, 0, (uint8_t*) packet, size); + return transport->send_packet(packet_type, (uint8_t *) packet, size); +} + +static int transport_send_cmd_va_arg(const hci_cmd_t *cmd, va_list argptr){ + uint8_t * packet = hci_outgoing; + uint16_t size = hci_cmd_create_from_template(packet, cmd, argptr); + return transport_send_packet(HCI_COMMAND_DATA_PACKET, packet, size); +} + +static int transport_send_cmd(const hci_cmd_t *cmd, ...){ + va_list argptr; + va_start(argptr, cmd); + int res = transport_send_cmd_va_arg(cmd, argptr); + va_end(argptr); + return res; +} + +static int transport_send_intel_secure(uint8_t fragment_type, const uint8_t * data, uint16_t len){ + little_endian_store_16(hci_outgoing, 0, 0xfc09); + hci_outgoing[2] = 1 + len; + hci_outgoing[3] = fragment_type; + memcpy(&hci_outgoing[4], data, len); + uint16_t size = 3 + 1 + len; + return transport_send_packet(HCI_ACL_DATA_PACKET, hci_outgoing, size); +} + +static int transport_send_intel_ddc(const uint8_t * data, uint16_t len){ + little_endian_store_16(hci_outgoing, 0, 0xfc8b); + hci_outgoing[2] = len; + memcpy(&hci_outgoing[3], data, len); + uint16_t size = 3 + len; + return transport_send_packet(HCI_COMMAND_DATA_PACKET, hci_outgoing, size); +} + +static void state_machine(uint8_t * packet); + +// read data from fw file and send it via intel_secure + update state +static int intel_send_fragment(uint8_t fragment_type, uint16_t len){ + int res = fread(fw_buffer, 1, len, fw_file); + log_info("offset %6u, read %3u -> res %d", fw_offset, len, res); + fw_offset += res; + state++; + return transport_send_intel_secure(fragment_type, fw_buffer, len); +} + +// read data from ddc file and send iva intel ddc command +// @returns -1 on eof +static int intel_send_ddc(void){ + int res; + // read len + res = fread(fw_buffer, 1, 1, fw_file); + log_info("offset %6u, read 1 -> res %d", fw_offset, res); + if (res == 0) return -1; + uint8_t len = fw_buffer[0]; + fw_offset += 1; + res = fread(&fw_buffer[1], 1, len, fw_file); + log_info("offset %6u, read %u -> res %d", fw_offset, 1, res); + return transport_send_intel_ddc(fw_buffer, 1 + len); +} + +static void dump_intel_version(intel_version_t * version){ + log_info("status 0x%02x", version->status); + log_info("hw_platform 0x%02x", version->hw_platform); + log_info("hw_variant 0x%02x", version->hw_variant); + log_info("hw_revision 0x%02x", version->hw_revision); + log_info("fw_variant 0x%02x", version->fw_variant); + log_info("fw_revision 0x%02x", version->fw_revision); + log_info("fw_build_num 0x%02x", version->fw_build_num); + log_info("fw_build_ww 0x%02x", version->fw_build_ww); + log_info("fw_build_yy 0x%02x", version->fw_build_yy); + log_info("fw_patch_num 0x%02x", version->fw_patch_num); +} + +static void dump_intel_boot_params(intel_boot_params_t * boot_params){ + bd_addr_t addr; + reverse_bd_addr(boot_params->otp_bdaddr, addr); + log_info("Device revision: %u", dev_revid); + log_info("Secure Boot: %s", boot_params->secure_boot ? "enabled" : "disabled"); + log_info("OTP lock: %s", boot_params->otp_lock ? "enabled" : "disabled"); + log_info("API lock: %s", boot_params->api_lock ? "enabled" : "disabled"); + log_info("Debug lock: %s", boot_params->debug_lock ? "enabled" : "disabled"); + log_info("Minimum firmware build %u week %u %u", boot_params->min_fw_build_nn, boot_params->min_fw_build_cw, 2000 + boot_params->min_fw_build_yy); + log_info("OTC BD_ADDR: %s", bd_addr_to_str(addr)); +} + +static int vendor_firmware_complete_received; +static int waiting_for_command_complete; + +static void state_machine(uint8_t * packet){ + intel_version_t * version; + intel_boot_params_t * boot_params; + char fw_name[30]; + int res; + uint16_t buffer_offset; + bd_addr_t addr; + + if (packet){ + // firmware upload complete event? + if (packet[0] == 0xff && packet[2] == 0x06) { + vendor_firmware_complete_received = 1; + } + + // command complete + if (packet[0] == 0x0e){ + waiting_for_command_complete = 0; + } + } + + switch (state){ + case 0: + state++; + transport_send_cmd(&hci_reset); + break; + case 1: + // Read Intel Version + state++; + transport_send_cmd(&hci_intel_read_version); + break; + case 2: + version = (intel_version_t*) hci_event_command_complete_get_return_parameters(packet); + dump_intel_version(version); + + hw_variant = version->hw_variant; + + // fw_variant = 0x06 bootloader mode / 0x23 operational mode + if (version->fw_variant == 0x23) { + (*done)(0); + break; + } + + if (version->fw_variant != 0x06){ + log_error("unknown fw_variant 0x%02x", version->fw_variant); + break; + } + + // Read Intel Secure Boot Params + state++; + transport_send_cmd(&hci_intel_read_secure_boot_params); + break; + case 3: + boot_params = (intel_boot_params_t *) hci_event_command_complete_get_return_parameters(packet); + dump_intel_boot_params(boot_params); + + reverse_bd_addr(boot_params->otp_bdaddr, addr); + dev_revid = little_endian_read_16((uint8_t*)&boot_params->dev_revid, 0); + + // assert commmand complete is required + if (boot_params->limited_cce != 0) break; + + // firmware file + snprintf(fw_name, sizeof(fw_name), "ibt-%u-%u.sfi", hw_variant, dev_revid); + log_info("Open firmware %s", fw_name); + printf("Firwmare %s\n", fw_name); + + // open firmware file + fw_offset = 0; + fw_file = fopen(fw_name, "rb"); + if (!fw_file){ + log_error("can't open file %s", fw_name); + (*done)(1); + return; + } + + vendor_firmware_complete_received = 0; + + // send CCS segment - offset 0 + intel_send_fragment(0x00, 128); + break; + case 4: + // send public key / part 1 - offset 128 + intel_send_fragment(0x03, 128); + break; + case 5: + // send public key / part 2 - offset 384 + intel_send_fragment(0x03, 128); + break; + case 6: + // skip 4 bytes + res = fread(fw_buffer, 1, 4, fw_file); + log_info("read res %d", res); + fw_offset += res; + + // send signature / part 1 - offset 388 + intel_send_fragment(0x02, 128); + break; + case 7: + // send signature / part 2 - offset 516 + intel_send_fragment(0x02, 128); + break; + case 8: + // send firmware chunks - offset 644 + // chunk len must be 4 byte aligned + // multiple commands can be combined + buffer_offset = 0; + do { + res = fread(&fw_buffer[buffer_offset], 1, 3, fw_file); + log_info("fw_offset %6u, buffer_offset %u, read %3u -> res %d", fw_offset, buffer_offset, 3, res); + fw_offset += res; + if (res == 0 ){ + // EOF + log_info("End of file"); + fclose(fw_file); + fw_file = NULL; + state++; + break; + } + int param_len = fw_buffer[buffer_offset + 2]; + buffer_offset += 3; + if (param_len){ + res = fread(&fw_buffer[buffer_offset], 1, param_len, fw_file); + fw_offset += res; + buffer_offset += res; + } + } while ((buffer_offset & 3) != 0); + + if (buffer_offset == 0) break; + + waiting_for_command_complete = 1; + transport_send_intel_secure(0x01, fw_buffer, buffer_offset); + break; + + case 9: + // expect Vendor Specific Event 0x06 + if (!vendor_firmware_complete_received) break; + + printf("Firmware upload complete\n"); + log_info("Vendor Event 0x06 - firmware complete"); + + // Reset Params - constants from Windows Intel driver + state++; + transport_send_cmd(&hci_intel_reset_param, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x04, 0x00); + break; + + case 10: + // expect Vendor Specific Event 0x02 + if (packet[0] != 0xff) break; + if (packet[2] != 0x02) break; + + printf("Firmware operational\n"); + log_info("Vendor Event 0x02 - firmware operational"); + + // Read Intel Version + state++; + transport_send_cmd(&hci_intel_read_version); + break; + + case 11: + version = (intel_version_t*) hci_event_command_complete_get_return_parameters(packet); + dump_intel_version(version); + + // ddc config + snprintf(fw_name, sizeof(fw_name), "ibt-%u-%u.ddc", hw_variant, dev_revid); + log_info("Open DDC %s", fw_name); + + // open ddc file + fw_offset = 0; + fw_file = fopen(fw_name, "rb"); + if (!fw_file){ + log_error("can't open file %s", fw_name); + + (*done)(1); + return; + } + + // load ddc + state++; + + /* fall through */ + + case 12: + res = intel_send_ddc(); + if (res == 0) break; + + // DDC download complete + state++; + log_info("Load DDC Complete"); + + + // Set Intel event mask 0xfc52 + state++; + transport_send_cmd(&hci_intel_set_event_mask, 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + break; + + case 13: + // 9F FC 01 00 + state++; + transport_send_cmd(&hci_intel_fc9f, 0x00); + break; + + case 14: + (*done)(0); + break; + + default: + break; + } +} + +static void transport_packet_handler (uint8_t packet_type, uint8_t *packet, uint16_t size){ + UNUSED(packet_type); + // we also get events with packet_type ACL from the controller + hci_dump_packet(HCI_EVENT_PACKET, 1, packet, size); + switch (hci_event_packet_get_type(packet)){ + case HCI_EVENT_COMMAND_COMPLETE: + case HCI_EVENT_VENDOR_SPECIFIC: + state_machine(packet); + break; + default: + break; + } +} + +void btstack_chipset_intel_download_firmware(const hci_transport_t * hci_transport, void (*callback)(int result)){ + + done = callback; + + transport = hci_transport;; + // transport->init(NULL); + transport->register_packet_handler(&transport_packet_handler); + transport->open(); + + // get started + state = 0; + state_machine(NULL); +} diff --git a/chipset/intel/btstack_chipset_intel_firmware.h b/chipset/intel/btstack_chipset_intel_firmware.h new file mode 100644 index 000000000..7ecd11362 --- /dev/null +++ b/chipset/intel/btstack_chipset_intel_firmware.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * btstack_chipset_intel_firmware.c + * + * Adapter to use Intel-based chipsets with BTstack + * + */ + +#ifndef __BTSTACK_CHIPSET_INTEL_FIRMWARE_H +#define __BTSTACK_CHIPSET_INTEL_FIRMWARE_H + +#include "hci_transport.h" + +#if defined __cplusplus +extern "C" { +#endif + + /** + * @brief Download firmware via hci_transport + * @param transport + * @param done callback. 0 = Success + */ +void btstack_chipset_intel_download_firmware(const hci_transport_t * hci_transport, void (*done)(int result)); + +#if defined __cplusplus +} +#endif + +#endif