diff --git a/tools/bluemoon.c b/tools/bluemoon.c
index d79f4a7..103532a 100644
--- a/tools/bluemoon.c
+++ b/tools/bluemoon.c
#include <ctype.h>
#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/param.h>
#include "monitor/mainloop.h"
#include "monitor/bt.h"
uint8_t rx_trace;
} __attribute__ ((packed));
+#define CMD_MEMORY_WRITE 0xfc8e
+
static struct bt_hci *hci_dev;
static uint16_t hci_index = 0;
+#define FIRMWARE_BASE_PATH "/lib/firmware"
+
static bool set_bdaddr = false;
static const char *set_bdaddr_value = NULL;
-
-static bool reset_on_exit = false;
-static bool use_manufacturer_mode = false;
static bool get_bddata = false;
+static bool load_firmware = false;
+static const char *load_firmware_value = NULL;
+static uint8_t *firmware_data = NULL;
+static size_t firmware_size = 0;
+static size_t firmware_offset = 0;
+uint8_t manufacturer_mode_reset = 0x00;
+static bool use_manufacturer_mode = false;
static bool set_traces = false;
+static bool reset_on_exit = false;
static void reset_complete(const void *data, uint8_t size, void *user_data)
{
{
bt_hci_flush(hci_dev);
+ free(firmware_data);
+
if (use_manufacturer_mode) {
struct cmd_manufacturer_mode cmd;
cmd.mode_switch = 0x00;
- cmd.reset = 0x00;
+ cmd.reset = manufacturer_mode_reset;
bt_hci_send(hci_dev, CMD_MANUFACTURER_MODE, &cmd, sizeof(cmd),
leave_manufacturer_mode_complete, NULL, NULL);
shutdown_device();
}
+static void firmware_command_complete(const void *data, uint8_t size,
+ void *user_data)
+{
+ uint8_t status = *((uint8_t *) data);
+
+ if (status) {
+ fprintf(stderr, "Failed to load firmware (0x%02x)\n", status);
+ manufacturer_mode_reset = 0x01;
+ shutdown_device();
+ return;
+ }
+
+ if (firmware_offset >= firmware_size) {
+ printf("Activating firmware\n");
+ manufacturer_mode_reset = 0x02;
+ shutdown_device();
+ return;
+ }
+
+ if (firmware_data[firmware_offset] == 0x01) {
+ uint16_t opcode;
+ uint8_t dlen;
+
+ opcode = firmware_data[firmware_offset + 2] << 8 |
+ firmware_data[firmware_offset + 1];
+ dlen = firmware_data[firmware_offset + 3];
+
+ bt_hci_send(hci_dev, opcode, firmware_data +
+ firmware_offset + 4, dlen,
+ firmware_command_complete, NULL, NULL);
+
+ firmware_offset += dlen + 4;
+
+ if (firmware_data[firmware_offset] == 0x02) {
+ dlen = firmware_data[firmware_offset + 2];
+ firmware_offset += dlen + 3;
+ }
+ } else {
+ fprintf(stderr, "Invalid packet in firmware\n");
+ manufacturer_mode_reset = 0x01;
+ shutdown_device();
+ }
+
+}
+
static void enter_manufacturer_mode_complete(const void *data, uint8_t size,
void *user_data)
{
return;
}
+ if (load_firmware) {
+ uint8_t status = BT_HCI_ERR_SUCCESS;
+ firmware_command_complete(&status, sizeof(status), NULL);
+ return;
+ }
+
if (get_bddata || set_bdaddr) {
bt_hci_send(hci_dev, CMD_READ_BD_DATA, NULL, 0,
read_bd_data_complete, NULL, NULL);
shutdown_device();
}
+static void request_firmware(const char *path)
+{
+ unsigned int cmd_num = 0;
+ unsigned int evt_num = 0;
+ struct stat st;
+ ssize_t len;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open firmware %s\n", path);
+ shutdown_device();
+ return;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ fprintf(stderr, "Failed to get firmware size\n");
+ close(fd);
+ shutdown_device();
+ return;
+ }
+
+ firmware_data = malloc(st.st_size);
+ if (!firmware_data) {
+ fprintf(stderr, "Failed to allocate firmware buffer\n");
+ close(fd);
+ shutdown_device();
+ return;
+ }
+
+ len = read(fd, firmware_data, st.st_size);
+ if (len < 0) {
+ fprintf(stderr, "Failed to read firmware file\n");
+ close(fd);
+ shutdown_device();
+ return;
+ }
+
+ close(fd);
+
+ if (len < st.st_size) {
+ fprintf(stderr, "Firmware size does not match buffer\n");
+ shutdown_device();
+ return;
+ }
+
+ firmware_size = len;
+
+ if (firmware_data[0] == 0xff)
+ firmware_offset = 1;
+
+ while (firmware_offset < firmware_size) {
+ uint16_t opcode;
+ uint8_t evt, dlen;
+
+ switch (firmware_data[firmware_offset]) {
+ case 0x01:
+ opcode = firmware_data[firmware_offset + 2] << 8 |
+ firmware_data[firmware_offset + 1];
+ dlen = firmware_data[firmware_offset + 3];
+
+ if (opcode != CMD_MEMORY_WRITE)
+ printf("Unexpected opcode 0x%02x\n", opcode);
+
+ firmware_offset += dlen + 4;
+ cmd_num++;
+ break;
+
+ case 0x02:
+ evt = firmware_data[firmware_offset + 1];
+ dlen = firmware_data[firmware_offset + 2];
+
+ if (evt != BT_HCI_EVT_CMD_COMPLETE)
+ printf("Unexpected event 0x%02x\n", evt);
+
+ firmware_offset += dlen + 3;
+ evt_num++;
+ break;
+
+ default:
+ fprintf(stderr, "Invalid firmware file\n");
+ shutdown_device();
+ return;
+ }
+ }
+
+ printf("Firmware with %u commands and %u events\n", cmd_num, evt_num);
+
+ if (firmware_data[0] == 0xff)
+ firmware_offset = 1;
+}
+
static void read_version_complete(const void *data, uint8_t size,
void *user_data)
{
return;
}
+ if (load_firmware) {
+ if (load_firmware_value) {
+ printf("Firmware: %s\n", load_firmware_value);
+ request_firmware(load_firmware_value);
+ } else {
+ char fw_name[PATH_MAX];
+
+ snprintf(fw_name, sizeof(fw_name),
+ "%s/%s/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.bseq",
+ FIRMWARE_BASE_PATH, "intel",
+ rsp->hw_platform, rsp->hw_variant,
+ rsp->hw_revision, rsp->fw_variant,
+ rsp->fw_revision, rsp->fw_build_nn,
+ rsp->fw_build_cw, rsp->fw_build_yy);
+
+ printf("Firmware: %s\n", fw_name);
+ printf("Patch level: %d\n", rsp->fw_patch);
+ request_firmware(fw_name);
+ }
+ }
+
if (use_manufacturer_mode) {
struct cmd_manufacturer_mode cmd;
printf("\tbluemoon [options]\n");
printf("Options:\n"
"\t-B, --bdaddr [addr] Set Bluetooth address\n"
+ "\t-F, --firwmare [file] Load firmware\n"
"\t-R, --reset Reset controller\n"
"\t-i, --index <num> Use specified controller\n"
"\t-h, --help Show help options\n");
static const struct option main_options[] = {
{ "bdaddr", optional_argument, NULL, 'A' },
{ "bddata", no_argument, NULL, 'D' },
+ { "firmware",optional_argument, NULL, 'F' },
{ "traces", no_argument, NULL, 'T' },
{ "reset", no_argument, NULL, 'R' },
{ "index", required_argument, NULL, 'i' },
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "A::DTRi:vh", main_options, NULL);
+ opt = getopt_long(argc, argv, "A::DF::TRi:vh",
+ main_options, NULL);
if (opt < 0)
break;
use_manufacturer_mode = true;
get_bddata = true;
break;
+ case 'F':
+ use_manufacturer_mode = true;
+ if (optarg)
+ load_firmware_value = optarg;
+ load_firmware = true;
+ break;
case 'T':
use_manufacturer_mode = true;
set_traces = true;