blob: d470f13178ccd89ec93c2c2e2403e4df386d8e5b [file] [log] [blame]
/*
* Copyright (c) 2019 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief HCI interface application
*/
#include <zephyr/kernel.h>
#include <zephyr/settings/settings.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/debug/stack.h>
#include <zephyr/net/buf.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/bluetooth/hci_vs.h>
#include <zephyr/bluetooth/hci_raw.h>
#include <zephyr/bluetooth/iso.h>
#include "edtt_driver.h"
#include "bs_tracing.h"
#include "commands.h"
#if IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
#define LOG_LEVEL LOG_LEVEL_DBG
#else
#define LOG_LEVEL CONFIG_BT_LOG_LEVEL
#endif
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(hci_test_app);
static uint16_t waiting_opcode;
static enum commands_t waiting_response;
static uint8_t m_events;
struct EdttIxit {
uint8_t refMajor;
uint8_t refMinor;
uint16_t len;
uint8_t *pVal;
};
/*! \brief Implementation eXtra Information for Test (IXIT) definitions */
#if defined(CONFIG_BT_CTLR_CONN_ISO_STREAMS_MAX_NSE)
const uint8_t TSPX_max_cis_nse = CONFIG_BT_CTLR_CONN_ISO_STREAMS_MAX_NSE;
#endif
/*! \brief Persistent LL IXIT values. */
static struct EdttIxit llIxits[] = {
#if defined(CONFIG_BT_CTLR_CONN_ISO_STREAMS_MAX_NSE)
{7, 14, 1, &TSPX_max_cis_nse},
#endif
};
/**
* @brief Clean out excess bytes from the input buffer
*/
static void read_excess_bytes(uint16_t size)
{
if (size > 0) {
uint8_t buffer[size];
edtt_read((uint8_t *)buffer, size, EDTTT_BLOCK);
LOG_ERR("command size wrong! (%u extra bytes removed)", size);
}
}
/**
* @brief Provide an error response when an HCI command send failed
*/
static void error_response(int error)
{
uint16_t response = sys_cpu_to_le16(waiting_response);
int le_error = sys_cpu_to_le32(error);
uint16_t size = sys_cpu_to_le16(sizeof(le_error));
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
edtt_write((uint8_t *)&le_error, sizeof(le_error), EDTTT_BLOCK);
waiting_response = CMD_NOTHING;
waiting_opcode = 0;
}
/**
* @brief Allocate buffer for HCI command and fill in opCode for the command
*/
static struct net_buf *hci_cmd_create(uint16_t opcode, uint8_t param_len)
{
struct bt_hci_cmd_hdr *hdr;
struct net_buf *buf;
buf = bt_buf_get_tx(BT_BUF_CMD, K_FOREVER, NULL, 0);
__ASSERT_NO_MSG(buf);
hdr = net_buf_add(buf, sizeof(*hdr));
hdr->opcode = sys_cpu_to_le16(opcode);
hdr->param_len = param_len;
return buf;
}
/**
* @brief Allocate buffer for ACL Data Package and fill in Header
*/
static struct net_buf *acl_data_create(struct bt_hci_acl_hdr *le_hdr)
{
struct net_buf *buf;
struct bt_hci_acl_hdr *hdr;
buf = bt_buf_get_tx(BT_BUF_ACL_OUT, K_FOREVER, NULL, 0);
__ASSERT_NO_MSG(buf);
hdr = net_buf_add(buf, sizeof(*hdr));
*hdr = *le_hdr;
return buf;
}
#if defined(CONFIG_BT_ISO)
/**
* @brief Allocate buffer for ISO Data Package and fill in Header
*/
static struct net_buf *iso_data_create(struct bt_hci_iso_hdr *le_hdr)
{
struct net_buf *buf;
struct bt_hci_iso_hdr *hdr;
buf = bt_buf_get_tx(BT_BUF_ISO_OUT, K_FOREVER, NULL, 0);
__ASSERT_NO_MSG(buf);
hdr = net_buf_add(buf, sizeof(*hdr));
*hdr = *le_hdr;
return buf;
}
#endif /* defined(CONFIG_BT_ISO) */
/**
* @brief Allocate buffer for HCI command, fill in parameters and send the
* command...
*/
static int send_hci_command(uint16_t opcode, uint8_t param_len, uint16_t response)
{
struct net_buf *buf;
void *cp;
int err = 0;
waiting_response = response;
buf = hci_cmd_create(waiting_opcode = opcode, param_len);
if (buf) {
if (param_len) {
cp = net_buf_add(buf, param_len);
edtt_read((uint8_t *)cp, param_len, EDTTT_BLOCK);
}
err = bt_send(buf);
if (err) {
LOG_ERR("Failed to send HCI command %d (err %d)",
opcode, err);
error_response(err);
}
} else {
LOG_ERR("Failed to create buffer for HCI command 0x%04x",
opcode);
error_response(-1);
}
return err;
}
/**
* @brief Echo function - echo input received...
*/
static void echo(uint16_t size)
{
uint16_t response = sys_cpu_to_le16(CMD_ECHO_RSP);
uint16_t le_size = sys_cpu_to_le16(size);
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
edtt_write((uint8_t *)&le_size, sizeof(le_size), EDTTT_BLOCK);
if (size > 0) {
uint8_t buff[size];
edtt_read(buff, size, EDTTT_BLOCK);
edtt_write(buff, size, EDTTT_BLOCK);
}
}
NET_BUF_POOL_FIXED_DEFINE(event_pool, 32, BT_BUF_RX_SIZE + 4, 4, NULL);
static K_FIFO_DEFINE(event_queue);
static K_FIFO_DEFINE(rx_queue);
NET_BUF_POOL_FIXED_DEFINE(data_pool, CONFIG_BT_CTLR_RX_BUFFERS + 14,
BT_BUF_ACL_SIZE(CONFIG_BT_BUF_ACL_TX_SIZE) + 4, 4, NULL);
static K_FIFO_DEFINE(data_queue);
#if defined(CONFIG_BT_ISO)
NET_BUF_POOL_FIXED_DEFINE(iso_data_pool, CONFIG_BT_ISO_RX_BUF_COUNT + 14,
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_RX_MTU) +
sizeof(uint32_t), 8, NULL);
static K_FIFO_DEFINE(iso_data_queue);
#endif /* CONFIG_BT_ISO */
/**
* @brief Handle Command Complete HCI event...
*/
static void command_complete(struct net_buf *buf)
{
struct bt_hci_evt_cmd_complete *evt = (void *)buf->data;
uint16_t opcode = sys_le16_to_cpu(evt->opcode);
uint16_t response = sys_cpu_to_le16(waiting_response);
struct net_buf_simple_state state;
uint16_t size;
net_buf_simple_save(&buf->b, &state);
net_buf_pull(buf, sizeof(*evt));
size = sys_cpu_to_le16(buf->len);
if (opcode == waiting_opcode) {
LOG_DBG("Command complete for 0x%04x", waiting_opcode);
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
edtt_write((uint8_t *)buf->data, buf->len, EDTTT_BLOCK);
waiting_opcode = 0;
} else {
LOG_WRN("Not waiting for 0x(%04x) command status,"
" expected 0x(%04x)", opcode, waiting_opcode);
}
net_buf_simple_restore(&buf->b, &state);
}
/**
* @brief Handle Command Status HCI event...
*/
static void command_status(struct net_buf *buf)
{
struct bt_hci_evt_cmd_status *evt = (void *)buf->data;
uint16_t opcode = sys_le16_to_cpu(evt->opcode);
uint16_t response = sys_cpu_to_le16(waiting_response);
struct net_buf_simple_state state;
uint8_t status = evt->status;
uint16_t size;
net_buf_simple_save(&buf->b, &state);
net_buf_pull(buf, sizeof(*evt));
size = sys_cpu_to_le16(buf->len) + 1;
if (opcode == waiting_opcode) {
LOG_DBG("Command status for 0x%04x", waiting_opcode);
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
edtt_write((uint8_t *)&status, sizeof(status), EDTTT_BLOCK);
edtt_write((uint8_t *)buf->data, buf->len, EDTTT_BLOCK);
waiting_opcode = 0;
} else {
LOG_WRN("Not waiting for 0x(%04x) command status,"
" expected 0x(%04x)", opcode, waiting_opcode);
}
net_buf_simple_restore(&buf->b, &state);
}
/**
* @brief Remove an event from the event queue
*/
static void discard_event(void)
{
struct net_buf *buf = net_buf_get(&event_queue, K_FOREVER);
net_buf_unref(buf);
m_events--;
}
/**
* @brief Allocate and store an event in the event queue
*/
static struct net_buf *queue_event(struct net_buf *buf)
{
struct net_buf *evt;
evt = net_buf_alloc(&event_pool, K_NO_WAIT);
if (evt) {
bt_buf_set_type(evt, BT_BUF_EVT);
net_buf_add_le32(evt, sys_cpu_to_le32(k_uptime_get()));
net_buf_add_mem(evt, buf->data, buf->len);
net_buf_put(&event_queue, evt);
m_events++;
}
return evt;
}
/**
* @brief Thread to service events and ACL/ISO data packets from the HCI input queue
*/
static void service_events(void *p1, void *p2, void *p3)
{
struct net_buf *buf, *evt;
while (1) {
buf = net_buf_get(&rx_queue, K_FOREVER);
if (bt_buf_get_type(buf) == BT_BUF_EVT) {
evt = queue_event(buf);
if (!evt) {
bs_trace_raw_time(4,
"Failed to allocated buffer "
"for event!\n");
LOG_WRN("No event in queue");
}
struct bt_hci_evt_hdr *hdr = (void *)buf->data;
net_buf_pull(buf, sizeof(*hdr));
switch (hdr->evt) {
case BT_HCI_EVT_CMD_COMPLETE:
if (!evt) {
discard_event();
evt = queue_event(buf);
}
command_complete(buf);
break;
case BT_HCI_EVT_CMD_STATUS:
if (!evt) {
discard_event();
evt = queue_event(buf);
}
command_status(buf);
break;
default:
break;
}
} else if (bt_buf_get_type(buf) == BT_BUF_ACL_IN) {
struct net_buf *data;
data = net_buf_alloc(&data_pool, K_NO_WAIT);
if (data) {
bt_buf_set_type(data, BT_BUF_ACL_IN);
net_buf_add_le32(data,
sys_cpu_to_le32(k_uptime_get()));
net_buf_add_mem(data, buf->data, buf->len);
net_buf_put(&data_queue, data);
}
#if defined(CONFIG_BT_ISO)
} else if (bt_buf_get_type(buf) == BT_BUF_ISO_IN) {
struct net_buf *data;
data = net_buf_alloc(&iso_data_pool, K_NO_WAIT);
if (data) {
bt_buf_set_type(data, BT_BUF_ISO_IN);
net_buf_add_le32(data,
sys_cpu_to_le32(k_uptime_get()));
net_buf_add_mem(data, buf->data, buf->len);
net_buf_put(&iso_data_queue, data);
}
#endif /* CONFIG_BT_ISO */
}
net_buf_unref(buf);
k_yield();
}
}
/**
* @brief Flush all HCI events from the input-copy queue
*/
static void flush_events(uint16_t size)
{
uint16_t response = sys_cpu_to_le16(CMD_FLUSH_EVENTS_RSP);
struct net_buf *buf;
while ((buf = net_buf_get(&event_queue, K_NO_WAIT))) {
net_buf_unref(buf);
m_events--;
}
read_excess_bytes(size);
size = 0;
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
}
/**
* @brief Get next available HCI event from the input-copy queue
*/
static void get_event(uint16_t size)
{
uint16_t response = sys_cpu_to_le16(CMD_GET_EVENT_RSP);
struct net_buf *buf;
read_excess_bytes(size);
size = 0;
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
buf = net_buf_get(&event_queue, K_FOREVER);
if (buf) {
size = sys_cpu_to_le16(buf->len);
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
edtt_write((uint8_t *)buf->data, buf->len, EDTTT_BLOCK);
net_buf_unref(buf);
m_events--;
} else {
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
}
}
/**
* @brief Get next available HCI events from the input-copy queue
*/
static void get_events(uint16_t size)
{
uint16_t response = sys_cpu_to_le16(CMD_GET_EVENT_RSP);
struct net_buf *buf;
uint8_t count = m_events;
read_excess_bytes(size);
size = 0;
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
edtt_write((uint8_t *)&count, sizeof(count), EDTTT_BLOCK);
while (count--) {
buf = net_buf_get(&event_queue, K_FOREVER);
size = sys_cpu_to_le16(buf->len);
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
edtt_write((uint8_t *)buf->data, buf->len, EDTTT_BLOCK);
net_buf_unref(buf);
m_events--;
}
}
/**
* @brief Check whether an HCI event is available in the input-copy queue
*/
static void has_event(uint16_t size)
{
struct has_event_resp {
uint16_t response;
uint16_t size;
uint8_t count;
} __packed;
struct has_event_resp le_response = {
.response = sys_cpu_to_le16(CMD_HAS_EVENT_RSP),
.size = sys_cpu_to_le16(1),
.count = m_events
};
if (size > 0) {
read_excess_bytes(size);
}
edtt_write((uint8_t *)&le_response, sizeof(le_response), EDTTT_BLOCK);
}
/**
* @brief Flush all ACL Data Packages from the input-copy queue
*/
static void le_flush_data(uint16_t size)
{
uint16_t response = sys_cpu_to_le16(CMD_LE_FLUSH_DATA_RSP);
struct net_buf *buf;
while ((buf = net_buf_get(&data_queue, K_NO_WAIT))) {
net_buf_unref(buf);
}
read_excess_bytes(size);
size = 0;
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
}
/**
* @brief Check whether an ACL Data Package is available in the input-copy queue
*/
static void le_data_ready(uint16_t size)
{
struct has_data_resp {
uint16_t response;
uint16_t size;
uint8_t empty;
} __packed;
struct has_data_resp le_response = {
.response = sys_cpu_to_le16(CMD_LE_DATA_READY_RSP),
.size = sys_cpu_to_le16(1),
.empty = 0
};
if (size > 0) {
read_excess_bytes(size);
}
if (k_fifo_is_empty(&data_queue)) {
le_response.empty = 1;
}
edtt_write((uint8_t *)&le_response, sizeof(le_response), EDTTT_BLOCK);
}
/**
* @brief Get next available HCI Data Package from the input-copy queue
*/
static void le_data_read(uint16_t size)
{
uint16_t response = sys_cpu_to_le16(CMD_LE_DATA_READ_RSP);
struct net_buf *buf;
read_excess_bytes(size);
size = 0;
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
buf = net_buf_get(&data_queue, K_FOREVER);
if (buf) {
size = sys_cpu_to_le16(buf->len);
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
edtt_write((uint8_t *)buf->data, buf->len, EDTTT_BLOCK);
net_buf_unref(buf);
} else {
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
}
}
/**
* @brief Write ACL Data Package to the Controller...
*/
static void le_data_write(uint16_t size)
{
struct data_write_resp {
uint16_t code;
uint16_t size;
uint8_t status;
} __packed;
struct data_write_resp response = {
.code = sys_cpu_to_le16(CMD_LE_DATA_WRITE_RSP),
.size = sys_cpu_to_le16(1),
.status = 0
};
struct net_buf *buf;
struct bt_hci_acl_hdr hdr;
int err;
if (size >= sizeof(hdr)) {
edtt_read((uint8_t *)&hdr, sizeof(hdr), EDTTT_BLOCK);
size -= sizeof(hdr);
buf = acl_data_create(&hdr);
if (buf) {
uint16_t hdr_length = sys_le16_to_cpu(hdr.len);
uint8_t *pdata = net_buf_add(buf, hdr_length);
if (size >= hdr_length) {
edtt_read(pdata, hdr_length, EDTTT_BLOCK);
size -= hdr_length;
}
err = bt_send(buf);
if (err) {
LOG_ERR("Failed to send ACL Data (err %d)",
err);
}
} else {
err = -2; /* Failed to allocate data buffer */
LOG_ERR("Failed to create buffer for ACL Data.");
}
} else {
/* Size too small for header (handle and data length) */
err = -3;
}
read_excess_bytes(size);
response.status = sys_cpu_to_le32(err);
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
}
#if defined(CONFIG_BT_ISO)
/**
* @brief Flush all ISO Data Packages from the input-copy queue
*/
static void le_flush_iso_data(uint16_t size)
{
uint16_t response = sys_cpu_to_le16(CMD_LE_FLUSH_ISO_DATA_RSP);
struct net_buf *buf;
while ((buf = net_buf_get(&iso_data_queue, K_NO_WAIT))) {
net_buf_unref(buf);
}
read_excess_bytes(size);
size = 0;
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
}
/**
* @brief Check whether an ISO Data Package is available in the input-copy queue
*/
static void le_iso_data_ready(uint16_t size)
{
struct has_iso_data_resp {
uint16_t response;
uint16_t size;
uint8_t empty;
} __packed;
struct has_iso_data_resp le_response = {
.response = sys_cpu_to_le16(CMD_LE_ISO_DATA_READY_RSP),
.size = sys_cpu_to_le16(1),
.empty = 0
};
if (size > 0) {
read_excess_bytes(size);
}
if (k_fifo_is_empty(&iso_data_queue)) {
le_response.empty = 1;
}
edtt_write((uint8_t *)&le_response, sizeof(le_response), EDTTT_BLOCK);
}
/**
* @brief Get next available ISO Data Package from the input-copy queue
*/
static void le_iso_data_read(uint16_t size)
{
uint16_t response = sys_cpu_to_le16(CMD_LE_ISO_DATA_READ_RSP);
struct net_buf *buf;
read_excess_bytes(size);
size = 0;
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
buf = net_buf_get(&iso_data_queue, K_FOREVER);
if (buf) {
size = sys_cpu_to_le16(buf->len);
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
edtt_write((uint8_t *)buf->data, buf->len, EDTTT_BLOCK);
net_buf_unref(buf);
} else {
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
}
}
/**
* @brief Write ISO Data Package to the Controller...
*/
static void le_iso_data_write(uint16_t size)
{
struct iso_data_write_resp {
uint16_t code;
uint16_t size;
uint8_t status;
} __packed;
struct iso_data_write_resp response = {
.code = sys_cpu_to_le16(CMD_LE_ISO_DATA_WRITE_RSP),
.size = sys_cpu_to_le16(1),
.status = 0
};
struct net_buf *buf;
struct bt_hci_iso_hdr hdr;
int err;
if (size >= sizeof(hdr)) {
edtt_read((uint8_t *)&hdr, sizeof(hdr), EDTTT_BLOCK);
size -= sizeof(hdr);
buf = iso_data_create(&hdr);
if (buf) {
uint16_t hdr_length = sys_le16_to_cpu(hdr.len);
uint8_t *pdata = net_buf_add(buf, hdr_length);
if (size >= hdr_length) {
edtt_read(pdata, hdr_length, EDTTT_BLOCK);
size -= hdr_length;
}
err = bt_send(buf);
if (err) {
LOG_ERR("Failed to send ISO Data (err %d)",
err);
}
} else {
err = -2; /* Failed to allocate data buffer */
LOG_ERR("Failed to create buffer for ISO Data.");
}
} else {
/* Size too small for header (handle and data length) */
err = -3;
}
read_excess_bytes(size);
response.status = sys_cpu_to_le32(err);
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
}
#endif /* CONFIG_BT_ISO */
/**
* @brief Read 'Implementation eXtra Information for Test' value
*/
static void le_ixit_value_read(uint16_t size)
{
uint8_t profileId;
uint8_t refMajor;
uint8_t refMinor;
struct EdttIxit *pIxitArray;
int ixitArraySize;
struct EdttIxit *pIxitElement = NULL;
/*
* CMD_GET_IXIT_VALUE_REQ payload layout
*
* ...
* [ 4] PROFILE_ID[0]
* [ 5] IXIT_Reference_Major
* [ 6] IXIT_Reference_Minor
*/
edtt_read((uint8_t *)&profileId, sizeof(profileId), EDTTT_BLOCK);
edtt_read((uint8_t *)&refMajor, sizeof(refMajor), EDTTT_BLOCK);
edtt_read((uint8_t *)&refMinor, sizeof(refMinor), EDTTT_BLOCK);
switch (profileId) {
case PROFILE_ID_LL:
pIxitArray = llIxits;
ixitArraySize = ARRAY_SIZE(llIxits);
break;
default:
pIxitArray = NULL;
ixitArraySize = 0;
}
for (int i = 0; i < ixitArraySize; i++) {
if (pIxitArray[i].refMajor == refMajor && pIxitArray[i].refMinor == refMinor) {
pIxitElement = &pIxitArray[i];
break;
}
}
struct ixit_value_get_resp {
uint16_t code;
uint16_t size;
uint8_t data[];
} __packed;
struct ixit_value_get_resp response = {
.code = sys_cpu_to_le16(CMD_GET_IXIT_VALUE_RSP),
.size = sys_cpu_to_le16(pIxitElement ? pIxitElement->len : 0),
};
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
if (pIxitElement) {
edtt_write(pIxitElement->pVal, pIxitElement->len, EDTTT_BLOCK);
}
}
static K_THREAD_STACK_DEFINE(service_events_stack,
CONFIG_BT_HCI_TX_STACK_SIZE);
static struct k_thread service_events_data;
/**
* @brief Zephyr application main entry...
*/
void main(void)
{
int err;
uint16_t command;
uint16_t size;
uint16_t opcode;
/**
* Initialize HCI command opcode and response variables...
*/
waiting_opcode = 0;
waiting_response = CMD_NOTHING;
m_events = 0;
/**
* Initialize Bluetooth stack in raw mode...
*/
err = bt_enable_raw(&rx_queue);
if (err) {
LOG_ERR("Bluetooth initialization failed (err %d)", err);
return;
}
/**
* Initialize and start EDTT system...
*/
#if defined(CONFIG_ARCH_POSIX)
enable_edtt_mode();
set_edtt_autoshutdown(true);
#endif
edtt_start();
/**
* Initialize and start thread to service HCI events and ACL data...
*/
k_thread_create(&service_events_data, service_events_stack,
K_THREAD_STACK_SIZEOF(service_events_stack),
service_events, NULL, NULL, NULL, K_PRIO_COOP(7),
0, K_NO_WAIT);
while (1) {
/**
* Wait for a command to arrive - then read and execute command
*/
edtt_read((uint8_t *)&command, sizeof(command), EDTTT_BLOCK);
command = sys_le16_to_cpu(command);
edtt_read((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
size = sys_le16_to_cpu(size);
bs_trace_raw_time(4, "command 0x%04X received (size %u) "
"events=%u\n",
command, size, m_events);
switch (command) {
case CMD_ECHO_REQ:
echo(size);
break;
case CMD_FLUSH_EVENTS_REQ:
flush_events(size);
break;
case CMD_HAS_EVENT_REQ:
has_event(size);
break;
case CMD_GET_EVENT_REQ:
{
uint8_t multiple;
edtt_read((uint8_t *)&multiple, sizeof(multiple),
EDTTT_BLOCK);
if (multiple)
get_events(--size);
else
get_event(--size);
}
break;
case CMD_LE_FLUSH_DATA_REQ:
le_flush_data(size);
break;
case CMD_LE_DATA_READY_REQ:
le_data_ready(size);
break;
case CMD_LE_DATA_WRITE_REQ:
le_data_write(size);
break;
case CMD_LE_DATA_READ_REQ:
le_data_read(size);
break;
#if defined(CONFIG_BT_ISO)
case CMD_LE_FLUSH_ISO_DATA_REQ:
le_flush_iso_data(size);
break;
case CMD_LE_ISO_DATA_READY_REQ:
le_iso_data_ready(size);
break;
case CMD_LE_ISO_DATA_WRITE_REQ:
le_iso_data_write(size);
break;
case CMD_LE_ISO_DATA_READ_REQ:
le_iso_data_read(size);
break;
#endif /* CONFIG_BT_ISO */
case CMD_GET_IXIT_VALUE_REQ:
le_ixit_value_read(size);
break;
default:
if (size >= 2) {
edtt_read((uint8_t *)&opcode, sizeof(opcode),
EDTTT_BLOCK);
send_hci_command(sys_le16_to_cpu(opcode),
size - 2, command + 1);
}
}
}
}