bcm-uart-hci firmware downloading
Change-Id: I649ab3730e76d9e87d5516d5b70985e9228c6bd6
diff --git a/system/dev/bluetooth/bcm-uart-hci/bcm-uart-hci.c b/system/dev/bluetooth/bcm-uart-hci/bcm-uart-hci.c
index ea491a6..e451bd9 100644
--- a/system/dev/bluetooth/bcm-uart-hci/bcm-uart-hci.c
+++ b/system/dev/bluetooth/bcm-uart-hci/bcm-uart-hci.c
@@ -25,7 +25,9 @@
// vendor command to switch baud rate to 2000000
const uint8_t SET_BAUD_RATE_CMD[] = { 0x18, 0xfc, 0x6, 0x0, 0x0, 0x80, 0x84, 0x1e, 0x0 };
-const uint8_t READ_LOCAL_NAME_CMD[] = { 0x14, 0xc, 0x0 };
+const uint8_t START_FIRMWARE_DOWNLOAD_CMD[] = { 0x2e, 0xfc, 0x0 };
+
+#define HCI_EVT_COMMAND_COMPLETE 0x0e
typedef struct {
zx_device_t* zxdev;
@@ -102,12 +104,15 @@
static zx_status_t bcm_hci_send_command(bcm_uart_hci_t* hci, const uint8_t* command, size_t length) {
- return zx_channel_write(hci->command_channel, 0, command, length, NULL, 0);
-}
+ // send HCI command
+ zx_status_t status = zx_channel_write(hci->command_channel, 0, command, length, NULL, 0);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "bcm_hci_send_command zx_channel_write failed %d\n", status);
+ return status;
+ }
-static zx_status_t bcm_hci_read_event(bcm_uart_hci_t* hci) {
+ // wait for command complete
uint8_t event_buf[255 + 2];
- zx_status_t status;
uint32_t actual;
do {
@@ -120,14 +125,27 @@
} while (status == ZX_ERR_SHOULD_WAIT);
if (status != ZX_OK) {
- zxlogf(ERROR, "bcm_hci_read_event zx_channel_read failed %d\n", status);
+ zxlogf(ERROR, "bcm_hci_send_command zx_channel_read failed %d\n", status);
+ return status;
}
- return status;
+
+ if (event_buf[0] != HCI_EVT_COMMAND_COMPLETE || event_buf[1] != 4 || event_buf[3] != command[0]
+ || event_buf[4] != command[1]) {
+ zxlogf(ERROR, "bcm_hci_send_command did not receive command complete\n");
+ return ZX_ERR_INTERNAL;
+ }
+ if (event_buf[5] != 0) {
+ zxlogf(ERROR, "bcm_hci_send_command got command complete error %u\n", event_buf[5]);
+ return ZX_ERR_INTERNAL;
+ }
+
+ return ZX_OK;
}
static int bcm_hci_start_thread(void* arg) {
bcm_uart_hci_t* hci = arg;
-
+ zx_handle_t fw_vmo;
+
zx_status_t status = bt_hci_open_command_channel(&hci->hci, &hci->command_channel);
if (status != ZX_OK) {
goto fail;
@@ -138,39 +156,83 @@
if (status != ZX_OK) {
goto fail;
}
- status = bcm_hci_read_event(hci);
- if (status != ZX_OK) {
- goto fail;
- }
// switch baud rate to 2000000
status = bcm_hci_send_command(hci, SET_BAUD_RATE_CMD, sizeof(SET_BAUD_RATE_CMD));
if (status != ZX_OK) {
goto fail;
}
- status = bcm_hci_read_event(hci);
- if (status != ZX_OK) {
- goto fail;
- }
status = serial_config(&hci->serial, 0, 2000000, SERIAL_SET_BAUD_RATE_ONLY);
if (status != ZX_OK) {
goto fail;
}
-
- // Send Read Local Name command
- status = bcm_hci_send_command(hci, READ_LOCAL_NAME_CMD, sizeof(READ_LOCAL_NAME_CMD));
- if (status != ZX_OK) {
- goto fail;
- }
- status = bcm_hci_read_event(hci);
- if (status != ZX_OK) {
- goto fail;
+
+ size_t fw_size;
+ status = load_firmware(hci->zxdev, "/boot/firmware/bt-firmware.bin", &fw_vmo, &fw_size);
+ if (status == ZX_OK) {
+ status = bcm_hci_send_command(hci, START_FIRMWARE_DOWNLOAD_CMD, sizeof(START_FIRMWARE_DOWNLOAD_CMD));
+ if (status != ZX_OK) {
+ goto fail;
+ }
+
+ zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
+
+ zx_off_t offset = 0;
+ while (offset < fw_size) {
+ uint8_t buffer[255 + 3];
+ size_t actual;
+
+ status = zx_vmo_read(fw_vmo, buffer, offset, sizeof(buffer), &actual);
+ if (status != ZX_OK) {
+ goto vmo_close_fail;
+ }
+ if (actual < 3) {
+ zxlogf(ERROR, "short HCI command in firmware download\n");
+ status = ZX_ERR_INTERNAL;
+ goto vmo_close_fail;
+ }
+ size_t length = buffer[2] + 3;
+ if (actual < length) {
+ zxlogf(ERROR, "short HCI command in firmware download\n");
+ status = ZX_ERR_INTERNAL;
+ goto vmo_close_fail;
+ }
+ status = bcm_hci_send_command(hci, buffer, length);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "bcm_hci_send_command failed in firmware download: %d\n", status);
+ goto vmo_close_fail;
+ }
+ offset += length;
+ }
+
+ zx_handle_close(fw_vmo);
+
+ // firmware switched us back to 115200. switch back to 2000000
+ status = serial_config(&hci->serial, 0, 115200, SERIAL_SET_BAUD_RATE_ONLY);
+ if (status != ZX_OK) {
+ goto fail;
+ }
+
+ // switch baud rate to 2000000 again
+ status = bcm_hci_send_command(hci, SET_BAUD_RATE_CMD, sizeof(SET_BAUD_RATE_CMD));
+ if (status != ZX_OK) {
+ goto fail;
+ }
+
+ status = serial_config(&hci->serial, 0, 2000000, SERIAL_SET_BAUD_RATE_ONLY);
+ if (status != ZX_OK) {
+ goto fail;
+ }
+ } else {
+ zxlogf(INFO, "bcm-uart-hci: no firmware file found\n");
}
device_make_visible(hci->zxdev);
return 0;
+vmo_close_fail:
+ zx_handle_close(fw_vmo);
fail:
zxlogf(ERROR, "bcm_hci_start_thread: device initialization failed: %d\n", status);
device_remove(hci->zxdev);