summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMyles Watson <mylesgw@chromium.org>2015-01-27 12:17:16 -0800
committerchrome-bot <chrome-bot@chromium.org>2016-08-08 01:48:45 -0700
commit4820a97ad5525aa99b1810a38ed0104e20914e56 (patch)
treee895b6714273289e6bf8cb98fe497ef0608aa27e
parent9ee1b4a767daaf23b278c9f33b0fe30bf9d91adf (diff)
downloadchrome-ec-4820a97ad5525aa99b1810a38ed0104e20914e56.tar.gz
btle: Add the HCI layer
Add a case statement to handle HCI commands. Add a test commands. Try to match the hcitool syntax, so the same commands can be executed on a Linux host. Added lcmd (long cmd) to pass more parameters in fewer arguments BUG=None BRANCH=None TEST=Use HCI commands to configure an advertiser and listen for it using `hcitool lescan` on the host. Change-Id: Ie28038847c9549eb1c27a605aa0fbad5efd3b2c7 Signed-off-by: Myles Watson <mylesgw@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/362145 Commit-Ready: Dan Shi <dshi@google.com> Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--board/hadoken/board.h1
-rw-r--r--common/btle_hci_controller.c672
-rw-r--r--common/build.mk2
-rw-r--r--include/config.h3
-rw-r--r--include/console_channel.inc3
5 files changed, 680 insertions, 1 deletions
diff --git a/board/hadoken/board.h b/board/hadoken/board.h
index ddbe81c4d2..f844013f15 100644
--- a/board/hadoken/board.h
+++ b/board/hadoken/board.h
@@ -29,6 +29,7 @@
#define CONFIG_BLUETOOTH_LE_STACK
#define CONFIG_BLUETOOTH_LE_RADIO_TEST
#define CONFIG_BLUETOOTH_LL_DEBUG
+#define CONFIG_BLUETOOTH_HCI_DEBUG
#include "gpio_signal.h"
diff --git a/common/btle_hci_controller.c b/common/btle_hci_controller.c
new file mode 100644
index 0000000000..49b189ce66
--- /dev/null
+++ b/common/btle_hci_controller.c
@@ -0,0 +1,672 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "btle_hci_int.h"
+#include "btle_hci2.h"
+#include "bluetooth_le_ll.h"
+#include "console.h"
+
+#ifdef CONFIG_BLUETOOTH_HCI_DEBUG
+
+#define CPUTS(outstr) cputs(CC_BLUETOOTH_HCI, outstr)
+#define CPRINTS(format, args...) cprints(CC_BLUETOOTH_HCI, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_BLUETOOTH_HCI, format, ## args)
+
+#else /* CONFIG_BLUETOOTH_HCI_DEBUG */
+
+#define CPUTS(outstr)
+#define CPRINTS(format, args...)
+#define CPRINTF(format, args...)
+
+#endif /* CONFIG_BLUETOOTH_HCI_DEBUG */
+
+static uint64_t hci_event_mask;
+static uint64_t hci_le_event_mask;
+
+#define MAX_MESSAGE 24
+
+#define STATUS (return_params[0])
+#define RPARAMS (&(return_params[1]))
+
+void hci_cmd(uint8_t *hciCmdbuf)
+{
+ static struct hciCmdHdr *hdr;
+ static uint8_t *params;
+ static uint8_t return_params[32];
+
+ uint8_t rparam_count = 1; /* Just status */
+ uint16_t event = HCI_EVT_Command_Complete; /* default */
+
+ STATUS = 0xff;
+
+ hdr = (struct hciCmdHdr *)hciCmdbuf;
+ params = hciCmdbuf + sizeof(struct hciCmdHdr);
+
+ CPRINTF("opcode %x OGF %d OCF %d\n", hdr->opcode,
+ CMD_GET_OGF(hdr->opcode), CMD_GET_OCF(hdr->opcode));
+ if (hdr->paramLen) {
+ int i;
+
+ CPRINTF("paramLen %d\n", hdr->paramLen);
+ for (i = 0; i < hdr->paramLen; i++)
+ CPRINTF("%x ", params[i]);
+ CPRINTF("\n");
+ }
+
+ switch (hdr->opcode) {
+ case CMD_MAKE_OPCODE(HCI_OGF_Controller_and_Baseband,
+ HCI_CMD_Reset):
+ STATUS = ll_reset();
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_Controller_and_Baseband,
+ HCI_CMD_Set_Event_Mask):
+ if (hdr->paramLen != sizeof(hci_event_mask))
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = HCI_SUCCESS;
+ memcpy(&hci_event_mask, params, sizeof(hci_event_mask));
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_Controller_and_Baseband,
+ HCI_CMD_Read_Transmit_Power_Level):
+ case CMD_MAKE_OPCODE(HCI_OGF_Informational,
+ HCI_CMD_Read_Local_Supported_Features):
+ case CMD_MAKE_OPCODE(HCI_OGF_Informational,
+ HCI_CMD_Read_Local_Supported_Commands):
+ case CMD_MAKE_OPCODE(HCI_OGF_Informational,
+ HCI_CMD_Read_Local_Version_Information):
+ case CMD_MAKE_OPCODE(HCI_OGF_Informational,
+ HCI_CMD_Read_BD_ADDR):
+ case CMD_MAKE_OPCODE(HCI_OGF_Link_Control,
+ HCI_CMD_Read_Remote_Version_Information):
+ case CMD_MAKE_OPCODE(HCI_OGF_Status,
+ HCI_CMD_Read_RSSI):
+ event = 0;
+ break;
+
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Event_Mask):
+ if (hdr->paramLen != sizeof(hci_le_event_mask))
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = HCI_SUCCESS;
+ memcpy(&hci_le_event_mask, params, sizeof(hci_le_event_mask));
+ break;
+
+ /* LE Information */
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Read_Buffer_Size):
+ if (hdr->paramLen != 0)
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_read_buffer_size(RPARAMS);
+ rparam_count = sizeof(struct hciCmplLeReadBufferSize);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Read_Local_Supported_Features):
+ if (hdr->paramLen != 0)
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_read_local_supported_features(RPARAMS);
+ rparam_count =
+ sizeof(struct hciCmplLeReadLocalSupportedFeatures);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Read_Supported_States):
+ if (hdr->paramLen != 0)
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_read_supported_states(RPARAMS);
+ rparam_count = sizeof(struct hciCmplLeReadSupportedStates);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Host_Channel_Classification):
+ if (hdr->paramLen !=
+ sizeof(struct hciLeSetHostChannelClassification))
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_set_host_channel_classification(params);
+ break;
+
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Random_Address):
+ if (hdr->paramLen != sizeof(struct hciLeSetRandomAddress))
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_set_random_address(params);
+ break;
+
+ /* Advertising */
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Advertise_Enable):
+ STATUS = ll_set_advertising_enable(params);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Advertising_Data):
+ STATUS = ll_set_adv_data(params);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Adv_Params):
+ if (hdr->paramLen != sizeof(struct hciLeSetAdvParams))
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_set_advertising_params(params);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Read_Adv_Channel_TX_Power):
+ STATUS = ll_read_tx_power();
+ rparam_count = sizeof(struct hciCmplLeReadAdvChannelTxPower);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Scan_Response_Data):
+ STATUS = ll_set_scan_response_data(params);
+ break;
+
+ /* Connections */
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Read_Remote_Used_Features):
+ if (hdr->paramLen != sizeof(struct hciLeReadRemoteUsedFeatures))
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_read_remote_used_features(params);
+ event = HCI_EVT_Command_Status;
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_Link_Control,
+ HCI_CMD_Disconnect):
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Connection_Update):
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Create_Connection):
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Create_Connection_Cancel):
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Read_Channel_Map):
+ event = 0;
+ break;
+
+ /* Encryption */
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Encrypt):
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_LTK_Request_Reply):
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_LTK_Request_Negative_Reply):
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Rand):
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Start_Encryption):
+ event = 0;
+ break;
+
+ /* Scanning */
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Scan_Enable):
+ if (hdr->paramLen != sizeof(struct hciLeSetScanEnable))
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_set_scan_enable(params);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Scan_Parameters):
+ if (hdr->paramLen != sizeof(struct hciLeSetScanParams))
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_set_scan_params(params);
+ break;
+
+ /* White List */
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Clear_White_List):
+ if (hdr->paramLen != 0)
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_clear_white_list();
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Read_White_List_Size):
+ if (hdr->paramLen != 0)
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_read_white_list_size(RPARAMS);
+ rparam_count = sizeof(struct hciCmplLeReadWhiteListSize);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Add_Device_To_White_List):
+ if (hdr->paramLen != sizeof(struct hciLeAddDeviceToWhiteList))
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_add_device_to_white_list(params);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Remove_Device_From_White_List):
+ if (hdr->paramLen !=
+ sizeof(struct hciLeRemoveDeviceFromWhiteList))
+ STATUS = HCI_ERR_Invalid_HCI_Command_Parameters;
+ else
+ STATUS = ll_remove_device_from_white_list(params);
+ break;
+
+ /* RFPHY Testing Support */
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Receiver_Test):
+ STATUS = ll_receiver_test(params);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Transmitter_Test):
+ STATUS = ll_transmitter_test(params);
+ break;
+ case CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Test_End):
+ STATUS = ll_test_end(RPARAMS);
+ rparam_count = sizeof(struct hciCmplLeTestEnd);
+ break;
+
+ default:
+ STATUS = HCI_ERR_Unknown_HCI_Command;
+ break;
+ }
+
+ hci_event(event, rparam_count, return_params);
+}
+
+void hci_acl_to_host(uint8_t *data, uint16_t hdr, uint16_t len)
+{
+ int i;
+
+ /* Enqueue hdr, len, len bytes of data */
+ CPRINTF("Sending %d bytes of data from handle %d with PB=%x.\n",
+ len, hdr & ACL_HDR_MASK_CONN_ID,
+ hdr & ACL_HDR_MASK_PB);
+ for (i = 0; i < len; i++)
+ CPRINTF("0x%x, ", data[i]);
+ CPRINTF("\n");
+}
+
+void hci_acl_from_host(uint8_t *hciAclbuf)
+{
+ struct hciAclHdr *hdr = (struct hciAclHdr *)hciAclbuf;
+ uint8_t *data = hciAclbuf + sizeof(struct hciAclHdr);
+ int i;
+
+ /* Send the data to the link layer */
+ CPRINTF("Sending %d bytes of data to handle %d with PB=%x.\n",
+ hdr->len, hdr->hdr & ACL_HDR_MASK_CONN_ID,
+ hdr->hdr & ACL_HDR_MASK_PB);
+ for (i = 0; i < hdr->len; i++)
+ CPRINTF("0x%x, ", data[i]);
+ CPRINTF("\n");
+}
+
+/*
+ * Required Events
+ *
+ * HCI_EVT_Command_Complete
+ * HCI_EVT_Command_Status
+ * HCI_EVTLE_Advertising_Report
+ * HCI_EVT_Disconnection_Complete
+ * HCI_EVTLE_Connection_Complete
+ * HCI_EVTLE_Connection_Update_Complete
+ * HCI_EVTLE_Read_Remote_Used_Features_Complete
+ * HCI_EVT_Number_Of_Completed_Packets
+ * HCI_EVT_Read_Remote_Version_Complete
+ * HCI_EVT_Encryption_Change
+ * HCI_EVT_Encryption_Key_Refresh_Complete
+ * HCI_EVTLE_Long_Term_Key_Request
+ */
+void hci_event(uint8_t event_code, uint8_t len, uint8_t *params)
+{
+ int i;
+
+ /* Copy it to the queue. */
+ CPRINTF("Event 0x%x len %d\n", event_code, len);
+ for (i = 0; i < len; i++)
+ CPRINTF("%x ", params[i]);
+ CPRINTF("\n");
+}
+
+#ifdef CONFIG_BLUETOOTH_HCI_DEBUG
+
+/*
+ * LE_Set_Advertising_Data
+ * hcitool lcmd 0x2008 19 0x42410907 0x46454443 0x3c11903 0x3050102 0x181203
+ * hcitool cmd 8 8 7 9 41 42 43 44 45 46 3 19 c1 3 2 1 5 3 3 12 18
+ *
+ * hcitool lcmd 0x2008 18 0x42410906 0x03454443 0x203c119 0x3030501 0x1812
+ * hcitool cmd 8 8 6 9 41 42 43 44 45 3 19 c1 3 2 1 5 3 3 12 18
+ */
+uint8_t adv0[19] = {0x07, 0x09, 'A', 'B', 'C', 'D', 'E', 'F', /* Name */
+ 0x03, 0x19, 0xc1, 0x03, /* Keyboard */
+ 0x02, 0x01, 0x05, /* Flags */
+ 0x03, 0x03, 0x12, 0x18}; /* UUID */
+
+uint8_t adv1[18] = {0x06, 0x09, 'A', 'B', 'C', 'D', 'E', /* Name */
+ 0x02, 0x01, 0x05, /* Flags */
+ 0x03, 0x19, 0xc1, 0x03, /* Keyboard */
+ 0x03, 0x03, 0x12, 0x18}; /* UUID */
+
+uint8_t *adverts[] = {adv0, adv1};
+uint8_t adv_lengths[] = {sizeof(adv0), sizeof(adv1)};
+
+uint8_t scan0[4] = {0x03, 0x08, 'A', 'B'}; /* Short Name */
+
+uint8_t scan1[] = {}; /* Empty */
+
+uint8_t *scans[] = {scan0, scan1};
+uint8_t scan_lengths[] = {sizeof(scan0), sizeof(scan1)};
+
+/*
+ * LE_Set_Adv_Params
+ * hcitool lcmd 0x2006 15 0x010000f0 0xb0010100 0xb4b3b2b1 0x0007c5
+ * hcitool cmd 8 6 f0 0 0 1 0 1 1 b0 b1 b2 b3 b4 c5 7 0
+ */
+uint8_t adv_param0[15] = {
+ 0xf0, 0x00, /* IntervalMin */
+ 0x00, 0x01, /* IntervalMax */
+ 0x00, /* Adv Type */
+ 0x01, /* Use Random Addr */
+ 0x01, /* Direct Random */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xc5, /* Direct Addr */
+ 0x07, /* Channel Map */
+ 0x00}; /* Filter Policy */
+
+uint8_t adv_param1[15] = {
+ 0xf0, 0x00, /* IntervalMin */
+ 0x00, 0x01, /* IntervalMax */
+ 0x02, /* Adv Type */
+ 0x01, /* Use Random Addr */
+ 0x01, /* Direct Random */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xc5, /* Direct Addr */
+ 0x07, /* Channel Map */
+ 0x00}; /* Filter Policy */
+
+uint8_t *adv_params[] = {adv_param0, adv_param1};
+
+/*
+ * LE Information
+ *
+ * LE Read Buffer Size
+ * hcitool cmd 8 2
+ *
+ * LE_Read_Local_Supported_Features
+ * hcitool cmd 8 3
+ *
+ * LE_Read_Supported_States
+ * hcitool cmd 8 1c
+ *
+ * LE_Set_Host_Channel_Classification
+ * hcitool cmd 8 14 0 1 2 3 4
+ * hcitool cmd 8 14 ff ff 02 ff 1f
+ */
+
+/*
+ * Scan commands:
+ *
+ * Set Scan Parameters:
+ * hcitool cmd 8 B 0 10 0 10 0 0 0 (passive 10 10 public all)
+ * hcitool lcmd 0x200B 7 0x10001000 0x0000 (passive 10 10 public all)
+ *
+ * hcitool cmd 8 B 1 30 0 20 0 1 1 (active 30 20 rand white)
+ * hcitool lcmd 0x200B 7 0x20003001 0x0101 (active 30 20 rand white)
+ *
+ * Set Scan Enable:
+ * hcitool cmd 8 C 0 0 (disabled)
+ * hcitool cmd 8 C 1 0 (enabled no_filtering)
+ * hcitool cmd 8 C 1 1 (enabled filter_duplicates)
+ *
+ */
+
+/* White list commands:
+ *
+ * Read White list size
+ * hcitool cmd 8 F
+ *
+ * Clear white list
+ * hcitool cmd 8 10
+ *
+ * Add device to white list (Public C5A4A3A2A1A0)
+ * hcitool cmd 8 11 0 a0 a1 a2 a3 a4 c5
+ * hcitool lcmd 0x2011 7 0xA2A1A000 0xC5A4A3
+ *
+ * Add device to white list (Random C5B4B3B2B1B0)
+ * hcitool cmd 8 11 1 b0 b1 b2 b4 b5 c5
+ * hcitool lcmd 0x2011 7 0xB2B1B001 0xC5B4B3
+ *
+ * Remove device from white list (Public C5A4A3A2A1A0)
+ * hcitool cmd 8 12 0 a0 a1 a2 a3 a4 c5
+ * hcitool lcmd 0x2012 7 0xA2A1A000 0xC5A4A3
+ *
+ * Remove device from white list (Random C5B4B3B2B1B0)
+ * hcitool cmd 8 12 1 b0 b1 b2 b4 b5 c5
+ * hcitool lcmd 0x2012 7 0xB2B1B001 0xC5B4B3
+ *
+ * Tested by checking dumping the white list and checking its size when:
+ * - adding devices
+ * - removing devices
+ * - removing non-existent devices
+ * - adding more than 8 devices
+ *
+ */
+
+/*
+ * Test commands:
+ *
+ * Rx Test channel 37
+ * hcitool cmd 8 1D 25
+ *
+ * Tx Test channel 37 20 bytes type 2
+ * hcitool cmd 8 1e 25 14 2
+ *
+ * Test end
+ * hcitool cmd 8 1f
+ */
+
+static uint8_t hci_buf[200];
+
+#define MAX_BLE_HCI_PARAMS 8
+static uint32_t param[MAX_BLE_HCI_PARAMS];
+
+static int command_ble_hci_cmd(int argc, char **argv)
+{
+ static struct hciCmdHdr header;
+ int length, opcode, i;
+ char *e;
+
+ if (argc < 3 || argc > MAX_BLE_HCI_PARAMS + 3)
+ return EC_ERROR_PARAM_COUNT;
+
+ opcode = strtoi(argv[1], &e, 0);
+ if (*e || opcode < 0 || opcode > 0xffff)
+ return EC_ERROR_PARAM1;
+
+ length = strtoi(argv[2], &e, 0);
+ if (*e || length < 0 || length > 32)
+ return EC_ERROR_PARAM2;
+
+ if ((length + 3) / 4 != argc - 3) {
+ CPRINTF("Remember to pass HCI params in 32-bit chunks.\n");
+ return EC_ERROR_PARAM_COUNT;
+ }
+
+ for (i = 3; i < argc; i++) {
+ param[i-3] = strtoi(argv[i], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM3 + i;
+ }
+
+ header.opcode = opcode;
+ header.paramLen = length;
+
+ memcpy(hci_buf, &header, sizeof(struct hciCmdHdr));
+ memcpy(hci_buf + sizeof(struct hciCmdHdr),
+ param, length);
+
+ hci_cmd(hci_buf);
+
+ CPRINTS("hci cmd @%p", hci_buf);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(ble_hci_cmd, command_ble_hci_cmd,
+ "opcode len uint32 uint32 uint32... (little endian)",
+ "Send an hci command of length len",
+ NULL);
+
+static int command_hcitool(int argc, char **argv)
+{
+ static struct hciCmdHdr header;
+ int i, ogf, ocf;
+ char *e;
+
+ if (argc < 4 || argc > MAX_BLE_HCI_PARAMS + 3)
+ return EC_ERROR_PARAM_COUNT;
+
+ if (argv[1][0] == 'l') /* strcmp lcmd */
+ return command_ble_hci_cmd(argc-1, &argv[1]);
+
+ ogf = strtoi(argv[2], &e, 16);
+ if (*e)
+ return EC_ERROR_PARAM2;
+
+ ocf = strtoi(argv[3], &e, 16);
+ if (*e)
+ return EC_ERROR_PARAM3;
+
+ header.opcode = CMD_MAKE_OPCODE(ogf, ocf);
+ header.paramLen = argc-4;
+ memcpy(hci_buf, &header, sizeof(struct hciCmdHdr));
+
+ for (i = 4; i < argc; i++) {
+ hci_buf[i - 4 + 3] = strtoi(argv[i], &e, 16);
+ if (*e)
+ return EC_ERROR_PARAM4 + i;
+ }
+
+ hci_cmd(hci_buf);
+
+ CPRINTS("hci cmd @%p", hci_buf);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(hcitool, command_hcitool,
+ "cmd ogf ocf b0 b1 b2 b3... or lcmd opcode len uint32.. (little endian)",
+ "Send an hci command of length len",
+ NULL);
+
+static int command_ble_hci_acl(int argc, char **argv)
+{
+ static struct hciAclHdr header;
+ int length, hdr, i;
+ char *e;
+
+ if (argc < 3 || argc > MAX_BLE_HCI_PARAMS + 3)
+ return EC_ERROR_PARAM_COUNT;
+
+ hdr = strtoi(argv[1], &e, 0);
+ if (*e || hdr < 0 || hdr > 0xffff)
+ return EC_ERROR_PARAM1;
+
+ length = strtoi(argv[2], &e, 0);
+ if (*e || length < 0 || length > 32)
+ return EC_ERROR_PARAM2;
+
+ if ((length + 3) / 4 != argc - 3) {
+ CPRINTF("Remember to pass HCI params in 32-bit chunks.\n");
+ return EC_ERROR_PARAM_COUNT;
+ }
+
+ for (i = 3; i < argc; i++) {
+ param[i-3] = strtoi(argv[i], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM3 + i;
+ }
+
+ header.hdr = hdr;
+ header.len = length;
+
+ memcpy(hci_buf, &header, sizeof(struct hciCmdHdr));
+ memcpy(hci_buf + sizeof(struct hciCmdHdr),
+ param, length);
+
+ hci_cmd(hci_buf);
+
+ CPRINTS("hci acl @%p", hci_buf);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(ble_hci_acl, command_ble_hci_acl,
+ "hdr len uint32 uint32 uint32... (little endian)",
+ "Send hci acl data of length len",
+ NULL);
+
+static int command_ble_hci_adv(int argc, char **argv)
+{
+ static struct hciCmdHdr header;
+ int adv, p = 0, scan_rsp = 0;
+ char *e;
+
+ if (argc < 2 || argc > 4)
+ return EC_ERROR_PARAM_COUNT;
+
+ adv = strtoi(argv[1], &e, 0);
+ if (*e || adv < 0 || adv > sizeof(adverts))
+ return EC_ERROR_PARAM1;
+
+ if (argc > 2) {
+ p = strtoi(argv[2], &e, 0);
+ if (*e || p < 0 || p > sizeof(adv_params))
+ return EC_ERROR_PARAM2;
+ }
+
+ if (argc > 3) {
+ scan_rsp = strtoi(argv[3], &e, 0);
+ if (*e || scan_rsp < 0 || scan_rsp > sizeof(scans))
+ return EC_ERROR_PARAM3;
+ }
+
+ header.opcode = CMD_MAKE_OPCODE(HCI_OGF_LE, HCI_CMD_LE_Set_Adv_Params);
+ header.paramLen = sizeof(struct hciLeSetAdvParams);
+
+ memcpy(hci_buf, &header, sizeof(struct hciCmdHdr));
+ memcpy(hci_buf + sizeof(struct hciCmdHdr),
+ adv_params[p], header.paramLen);
+
+ hci_cmd(hci_buf);
+
+ header.opcode = CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Advertising_Data);
+ header.paramLen = adv_lengths[adv];
+
+ memcpy(hci_buf, &header, sizeof(struct hciCmdHdr));
+ memcpy(hci_buf + sizeof(struct hciCmdHdr),
+ adverts[adv], header.paramLen);
+
+ hci_cmd(hci_buf);
+
+ header.opcode = CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Scan_Response_Data);
+ header.paramLen = scan_lengths[scan_rsp];
+
+ memcpy(hci_buf, &header, sizeof(struct hciCmdHdr));
+ memcpy(hci_buf + sizeof(struct hciCmdHdr),
+ scans[scan_rsp], header.paramLen);
+
+ hci_cmd(hci_buf);
+
+ header.opcode = CMD_MAKE_OPCODE(HCI_OGF_LE,
+ HCI_CMD_LE_Set_Advertise_Enable);
+ header.paramLen = sizeof(struct hciLeSetAdvEnable);
+
+ memcpy(hci_buf, &header, sizeof(struct hciCmdHdr));
+ hci_buf[sizeof(struct hciCmdHdr)] = 1;
+
+ hci_cmd(hci_buf);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(ble_hci_adv, command_ble_hci_adv,
+ "adv [params=0] [scan_rsp=0]",
+ "Use pre-defined parameters to start advertising",
+ NULL);
+
+#endif /* CONFIG_BLUETOOTH_HCI_DEBUG */
diff --git a/common/build.mk b/common/build.mk
index 09700021ec..16a7aa6353 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -26,7 +26,7 @@ common-$(CONFIG_BATTERY_BQ27541)+=battery.o
common-$(CONFIG_BATTERY_BQ27621)+=battery.o
common-$(CONFIG_BATTERY_SMART)+=battery.o
common-$(CONFIG_BLUETOOTH_LE)+=bluetooth_le.o
-common-$(CONFIG_BLUETOOTH_LE_STACK)+= btle_ll.o
+common-$(CONFIG_BLUETOOTH_LE_STACK)+=btle_hci_controller.o btle_ll.o
common-$(CONFIG_BUTTON_COUNT)+=button.o
common-$(CONFIG_CAPSENSE)+=capsense.o
common-$(CONFIG_CASE_CLOSED_DEBUG)+=case_closed_debug.o
diff --git a/include/config.h b/include/config.h
index 7180757350..6a89266106 100644
--- a/include/config.h
+++ b/include/config.h
@@ -265,6 +265,9 @@
/* Include debugging support for the Bluetooth link layer */
#undef CONFIG_BLUETOOTH_LL_DEBUG
+/* Include debugging support for Bluetooth HCI */
+#undef CONFIG_BLUETOOTH_HCI_DEBUG
+
/* Boot header storage offset. */
#undef CONFIG_BOOT_HEADER_STORAGE_OFF
diff --git a/include/console_channel.inc b/include/console_channel.inc
index ab733bd49b..ba48a31563 100644
--- a/include/console_channel.inc
+++ b/include/console_channel.inc
@@ -12,6 +12,9 @@ CONSOLE_CHANNEL(CC_BLUETOOTH_LE, "bluetooth_le")
#ifdef CONFIG_BLUETOOTH_LL_DEBUG
CONSOLE_CHANNEL(CC_BLUETOOTH_LL, "bluetooth_ll")
#endif
+#ifdef CONFIG_BLUETOOTH_HCI_DEBUG
+CONSOLE_CHANNEL(CC_BLUETOOTH_HCI,"bluetooth_hci")
+#endif
#endif /* CONFIG_BLUETOOTH_LE */
#ifdef CONFIG_EXTENSION_COMMAND
CONSOLE_CHANNEL(CC_EXTENSION, "extension")