/*
|
* Copyright (c) 2019 Oticon A/S
|
* Copyright (c) 2021 Codecoup
|
*
|
* SPDX-License-Identifier: Apache-2.0
|
*/
|
|
#include <assert.h>
|
#include <string.h>
|
#include <stdint.h>
|
#include <bs_tracing.h>
|
#include "sysinit/sysinit.h"
|
#include "nimble_syscfg.h"
|
#include "os/os_eventq.h"
|
#include "controller/ble_ll.h"
|
|
/* BLE */
|
#include "nimble/ble.h"
|
#include "nimble/hci_common.h"
|
|
#include "bs_symbols.h"
|
#include "bs_types.h"
|
#include "time_machine.h"
|
#include "ble_hci_edtt.h"
|
#include "edtt_driver.h"
|
#include "commands.h"
|
|
#define BLE_HCI_EDTT_NONE 0x00
|
#define BLE_HCI_EDTT_CMD 0x01
|
#define BLE_HCI_EDTT_ACL 0x02
|
#define BLE_HCI_EDTT_EVT 0x04
|
|
#define BT_HCI_OP_VS_WRITE_BD_ADDR 0xFC06
|
|
/* A packet for queueing EDTT/HCI commands and events */
|
struct ble_hci_edtt_pkt {
|
struct os_event ev;
|
STAILQ_ENTRY(ble_hci_edtt_pkt) next;
|
uint32_t timestamp;
|
uint8_t type;
|
void *data;
|
};
|
|
static struct os_eventq edtt_q_svc;
|
static struct os_eventq edtt_q_data;
|
static struct os_eventq edtt_q_event;
|
static uint8_t edtt_q_event_count;
|
|
static uint16_t waiting_opcode;
|
static enum commands_t waiting_response;
|
|
static struct os_task edtt_poller_task;
|
static struct os_task edtt_service_task;
|
|
#if EDTT_HCI_LOGS
|
extern unsigned int global_device_nbr;
|
static FILE *fptr;
|
|
static void
|
log_hex_array(uint8_t *buf, int len)
|
{
|
int i;
|
for (i = 0; i < len; i++) {
|
fprintf(fptr, "%02X ", buf[i]);
|
}
|
}
|
|
static void
|
log_hci_cmd(uint16_t opcode, uint8_t *buf, int len)
|
{
|
if (fptr) {
|
fprintf(fptr, "> %llu %02d %02d ", now, BLE_HCI_OCF(opcode), (BLE_HCI_OGF(opcode)));
|
log_hex_array(buf, len);
|
fputs("\n\n", fptr);
|
fflush(fptr);
|
}
|
}
|
|
static void
|
log_hci_evt(struct ble_hci_ev *hdr)
|
{
|
if (fptr) {
|
fprintf(fptr, "< %llu %02d ", now, hdr->opcode);
|
log_hex_array(hdr->data, hdr->length);
|
fputs("\n\n", fptr);
|
fflush(fptr);
|
}
|
}
|
|
static void
|
log_hci_init()
|
{
|
int flen = (int) strlen(MYNEWT_VAL(EDTT_HCI_LOG_FILE)) + 7;
|
char *fpath = (char *) calloc(flen, sizeof(char));
|
sprintf(fpath, "%s_%02d.log", MYNEWT_VAL(EDTT_HCI_LOG_FILE), global_device_nbr);
|
fptr = fopen(fpath, "w");
|
free(fpath);
|
}
|
#endif
|
|
static int
|
ble_hci_edtt_acl_tx(struct os_mbuf *om)
|
{
|
struct ble_hci_edtt_pkt *pkt;
|
|
/* If this packet is zero length, just free it */
|
if (OS_MBUF_PKTLEN(om) == 0) {
|
os_mbuf_free_chain(om);
|
return 0;
|
}
|
|
pkt = calloc(1, sizeof(*pkt));
|
pkt->type = BLE_HCI_EDTT_ACL;
|
pkt->data = om;
|
|
os_eventq_put(&edtt_q_svc, &pkt->ev);
|
|
return 0;
|
}
|
|
static int
|
ble_hci_edtt_cmdevt_tx(uint8_t *hci_ev, uint8_t edtt_type)
|
{
|
struct ble_hci_edtt_pkt *pkt;
|
|
pkt = calloc(1, sizeof(*pkt));
|
pkt->type = edtt_type;
|
pkt->data = hci_ev;
|
|
os_eventq_put(&edtt_q_svc, &pkt->ev);
|
|
return 0;
|
}
|
|
/**
|
* Sends an HCI event from the controller to the host.
|
*
|
* @param cmd The HCI event to send. This buffer must be
|
* allocated via ble_hci_trans_buf_alloc().
|
*
|
* @return 0 on success;
|
* A BLE_ERR_[...] error code on failure.
|
*/
|
int
|
ble_transport_to_hs_evt(void *buf)
|
{
|
return ble_hci_edtt_cmdevt_tx(buf, BLE_HCI_EDTT_EVT);
|
}
|
|
/**
|
* Sends ACL data from controller to host.
|
*
|
* @param om The ACL data packet to send.
|
*
|
* @return 0 on success;
|
* A BLE_ERR_[...] error code on failure.
|
*/
|
int
|
ble_transport_to_hs_acl(struct os_mbuf *om)
|
{
|
return ble_hci_edtt_acl_tx(om);
|
}
|
|
/**
|
* @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);
|
bs_trace_raw_time(3, "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 = waiting_response;
|
int le_error = error;
|
uint16_t size = 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, fill in parameters and send the
|
* command
|
*/
|
static int
|
send_hci_cmd_to_ctrl(uint16_t opcode, uint8_t param_len, uint16_t response) {
|
struct ble_hci_cmd *buf;
|
int err = 0;
|
waiting_response = response;
|
waiting_opcode = opcode;
|
|
buf = ble_transport_alloc_cmd();
|
if (buf != NULL) {
|
buf->opcode = opcode;
|
buf->length = param_len;
|
|
if (param_len) {
|
edtt_read((uint8_t *) buf->data, param_len, EDTTT_BLOCK);
|
}
|
|
#if EDTT_HCI_LOGS
|
log_hci_cmd(opcode, buf->data, param_len);
|
#endif
|
|
err = ble_transport_to_ll_cmd(buf);
|
if (err) {
|
ble_transport_free(buf);
|
bs_trace_raw_time(3, "Failed to send HCI command %d (err %d)", opcode, err);
|
error_response(err);
|
}
|
} else {
|
bs_trace_raw_time(3, "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 = CMD_ECHO_RSP;
|
|
edtt_write((uint8_t *) &response, sizeof(response), EDTTT_BLOCK);
|
edtt_write((uint8_t *) &size, sizeof(size), EDTTT_BLOCK);
|
|
if (size > 0) {
|
uint8_t buff[size];
|
|
edtt_read(buff, size, EDTTT_BLOCK);
|
edtt_write(buff, size, EDTTT_BLOCK);
|
}
|
}
|
|
/**
|
* @brief Handle Command Complete HCI event
|
*/
|
static void
|
command_complete(struct ble_hci_ev *evt)
|
{
|
struct ble_hci_ev_command_complete *evt_cc = (void *) evt->data;
|
uint16_t response = waiting_response;
|
uint16_t size = evt->length - sizeof(evt_cc->num_packets) - sizeof(evt_cc->opcode);
|
|
if (evt_cc->opcode == 0) {
|
/* ignore nop */
|
} else if (evt_cc->opcode == waiting_opcode) {
|
bs_trace_raw_time(9, "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 *) &evt_cc->status, sizeof(evt_cc->status), EDTTT_BLOCK);
|
edtt_write((uint8_t *) &evt_cc->return_params, size - sizeof(evt_cc->status), EDTTT_BLOCK);
|
waiting_opcode = 0;
|
} else {
|
bs_trace_raw_time(5, "Not waiting for 0x(%04x) command status,"
|
" expected 0x(%04x)", evt_cc->opcode, waiting_opcode);
|
}
|
}
|
|
/**
|
* @brief Handle Command Status HCI event
|
*/
|
static void
|
command_status(struct ble_hci_ev *evt)
|
{
|
struct ble_hci_ev_command_status *evt_cs = (void *) evt->data;
|
uint16_t opcode = evt_cs->opcode;
|
uint16_t response = waiting_response;
|
uint16_t size;
|
|
size = evt->length - sizeof(evt_cs->num_packets) - sizeof(evt_cs->opcode);
|
|
if (opcode == waiting_opcode) {
|
bs_trace_raw_time(9, "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 *) &evt_cs->status, sizeof(evt_cs->status), EDTTT_BLOCK);
|
edtt_write((uint8_t *) &evt_cs->num_packets, size - sizeof(evt_cs->status), EDTTT_BLOCK);
|
waiting_opcode = 0;
|
} else {
|
bs_trace_raw_time(5, "Not waiting for 0x(%04x) command status,"
|
" expected 0x(%04x)", opcode, waiting_opcode);
|
}
|
}
|
|
static void
|
free_data(struct ble_hci_edtt_pkt *pkt)
|
{
|
assert(pkt);
|
os_mbuf_free_chain(pkt->data);
|
free(pkt);
|
}
|
|
static void
|
free_event(struct ble_hci_edtt_pkt *pkt)
|
{
|
assert(pkt);
|
ble_transport_free(pkt->data);
|
free(pkt);
|
}
|
|
/**
|
* @brief Allocate and store an event in the event queue
|
*/
|
static struct ble_hci_edtt_pkt *
|
queue_event(struct ble_hci_ev *evt)
|
{
|
struct ble_hci_edtt_pkt *pkt;
|
|
pkt = calloc(1, sizeof(*pkt));
|
assert(pkt);
|
pkt->timestamp = tm_get_hw_time();
|
pkt->type = BLE_HCI_EDTT_EVT;
|
pkt->data = evt;
|
|
os_eventq_put(&edtt_q_event, &pkt->ev);
|
edtt_q_event_count++;
|
|
return pkt;
|
}
|
|
static struct ble_hci_edtt_pkt *
|
queue_data(struct os_mbuf *om)
|
{
|
struct ble_hci_edtt_pkt *pkt;
|
|
pkt = calloc(1, sizeof(*pkt));
|
assert(pkt);
|
pkt->timestamp = tm_get_hw_time();
|
pkt->type = BLE_HCI_EDTT_ACL;
|
pkt->data = om;
|
|
os_eventq_put(&edtt_q_data, &pkt->ev);
|
|
return pkt;
|
}
|
|
|
static void *
|
dup_complete_evt(void *evt)
|
{
|
struct ble_hci_ev *evt_copy;
|
|
/* max evt size is always 257 */
|
evt_copy = ble_transport_alloc_evt(0);
|
memcpy(evt_copy, evt, 257);
|
ble_transport_free(evt);
|
|
return evt_copy;
|
}
|
|
/**
|
* @brief Thread to service events and ACL data packets from the HCI input queue
|
*/
|
static void
|
service_events(void *arg)
|
{
|
struct ble_hci_edtt_pkt *pkt;
|
struct ble_hci_ev *evt;
|
struct ble_hci_ev_num_comp_pkts *evt_ncp;
|
|
while (1) {
|
pkt = (void *)os_eventq_get(&edtt_q_svc);
|
|
if (pkt->type == BLE_HCI_EDTT_EVT) {
|
evt = (void *)pkt->data;
|
|
#if EDTT_HCI_LOGS
|
log_hci_evt(hdr);
|
#endif
|
|
/* Prepare and send EDTT events */
|
switch (evt->opcode) {
|
case BLE_HCI_EVCODE_COMMAND_COMPLETE:
|
evt = dup_complete_evt(evt);
|
queue_event(evt);
|
command_complete(evt);
|
break;
|
case BLE_HCI_EVCODE_COMMAND_STATUS:
|
evt = dup_complete_evt(evt);
|
queue_event(evt);
|
command_status(evt);
|
break;
|
case BLE_HCI_EVCODE_NUM_COMP_PKTS:
|
evt_ncp = (void *)evt->data;
|
/* This should always be true for NimBLE LL */
|
assert(evt_ncp->count == 1);
|
if (evt_ncp->completed[0].packets == 0) {
|
/* Discard, because EDTT does not like it */
|
ble_transport_free(evt);
|
} else {
|
queue_event(evt);
|
}
|
break;
|
case BLE_HCI_OPCODE_NOP:
|
/* Ignore noop bytes from Link layer */
|
ble_transport_free(evt);
|
break;
|
default:
|
/* Queue HCI events. We will send them to EDTT
|
* on CMD_GET_EVENT_REQ. */
|
queue_event(evt);
|
}
|
} else if (pkt->type == BLE_HCI_EDTT_ACL) {
|
queue_data(pkt->data);
|
}
|
|
free(pkt);
|
}
|
}
|
|
/**
|
* @brief Flush all HCI events from the input-copy queue
|
*/
|
static void
|
flush_events(uint16_t size)
|
{
|
uint16_t response = CMD_FLUSH_EVENTS_RSP;
|
struct ble_hci_edtt_pkt *pkt;
|
|
while ((pkt = (void *)os_eventq_get_no_wait(&edtt_q_event))) {
|
free_event(pkt);
|
edtt_q_event_count--;
|
}
|
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 = CMD_GET_EVENT_RSP;
|
struct ble_hci_edtt_pkt *pkt;
|
struct ble_hci_ev *evt;
|
|
read_excess_bytes(size);
|
size = 0;
|
|
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
|
pkt = (void*)os_eventq_get(&edtt_q_event);
|
if (pkt) {
|
evt = pkt->data;
|
size = sizeof(pkt->timestamp) + sizeof(*evt) + evt->length;
|
|
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
|
edtt_write((uint8_t *)&pkt->timestamp, sizeof(pkt->timestamp), EDTTT_BLOCK);
|
edtt_write((uint8_t *)evt, sizeof(*evt) + evt->length, EDTTT_BLOCK);
|
|
free_event(pkt);
|
|
edtt_q_event_count--;
|
} 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 = CMD_GET_EVENT_RSP;
|
struct ble_hci_edtt_pkt *pkt;
|
struct ble_hci_ev *evt;
|
uint8_t count = edtt_q_event_count;
|
|
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--) {
|
pkt = (void *)os_eventq_get_no_wait(&edtt_q_event);
|
assert(pkt);
|
evt = pkt->data;
|
size = sizeof(pkt->timestamp) + sizeof(*evt) + evt->length;
|
|
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
|
edtt_write((uint8_t *)&pkt->timestamp, sizeof(pkt->timestamp), EDTTT_BLOCK);
|
edtt_write((uint8_t *)evt, sizeof(*evt) + evt->length, EDTTT_BLOCK);
|
|
free_event(pkt);
|
|
edtt_q_event_count--;
|
}
|
}
|
|
/**
|
* @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;
|
} __attribute__((packed));
|
struct has_event_resp le_response = {
|
.response = CMD_HAS_EVENT_RSP,
|
.size = 1,
|
.count = edtt_q_event_count
|
};
|
|
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 = CMD_LE_FLUSH_DATA_RSP;
|
struct ble_hci_edtt_pkt *pkt;
|
|
while ((pkt = (void *)os_eventq_get_no_wait(&edtt_q_data))) {
|
free_data(pkt);
|
}
|
|
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;
|
} __attribute__((packed));
|
struct has_data_resp le_response = {
|
.response = CMD_LE_DATA_READY_RSP,
|
.size = 1,
|
.empty = 0
|
};
|
|
if (size > 0) {
|
read_excess_bytes(size);
|
}
|
|
/* There's no API to check if eventq is empty but a little hack will do... */
|
if (edtt_q_data.evq_list.stqh_first == NULL) {
|
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 = CMD_LE_DATA_READ_RSP;
|
struct ble_hci_edtt_pkt *pkt;
|
struct os_mbuf *om;
|
|
read_excess_bytes(size);
|
size = 0;
|
|
edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
|
pkt = (void *)os_eventq_get(&edtt_q_data);
|
if (pkt) {
|
om = pkt->data;
|
|
size = sizeof(pkt->timestamp) + OS_MBUF_PKTLEN(om);
|
|
edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
|
edtt_write((uint8_t *)&pkt->timestamp, sizeof(pkt->timestamp), EDTTT_BLOCK);
|
|
while (om != NULL) {
|
edtt_write((uint8_t *)om->om_data, om->om_len, EDTTT_BLOCK);
|
om = SLIST_NEXT(om, om_next);
|
}
|
|
free_data(pkt);
|
} 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;
|
} __attribute__((packed));
|
struct data_write_resp response = {
|
.code = CMD_LE_DATA_WRITE_RSP,
|
.size = 1,
|
.status = 0
|
};
|
struct os_mbuf *om;
|
struct hci_data_hdr hdr;
|
int err;
|
|
if (size >= sizeof(hdr)) {
|
om = ble_transport_alloc_acl_from_hs();
|
if (om) {
|
edtt_read((void *)&hdr, sizeof(hdr), EDTTT_BLOCK);
|
size -= sizeof(hdr);
|
|
os_mbuf_append(om, &hdr, sizeof(hdr));
|
|
if (size >= hdr.hdh_len) {
|
/* Don't care, we have plenty of stack */
|
uint8_t tmp[hdr.hdh_len];
|
|
edtt_read(tmp, hdr.hdh_len, EDTTT_BLOCK);
|
size -= hdr.hdh_len;
|
|
os_mbuf_append(om, tmp, hdr.hdh_len);
|
}
|
|
err = ble_transport_to_ll_acl(om);
|
if (err) {
|
bs_trace_raw_time(3, "Failed to send ACL Data (err %d)", err);
|
}
|
} else {
|
err = -2; /* Failed to allocate data buffer */
|
bs_trace_raw_time(3, "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 = err;
|
edtt_write((uint8_t *) &response, sizeof(response), EDTTT_BLOCK);
|
}
|
|
static void
|
fake_write_bd_addr_cc()
|
{
|
struct ble_hci_ev_command_complete *ev;
|
struct ble_hci_ev *hci_ev;
|
waiting_opcode = BT_HCI_OP_VS_WRITE_BD_ADDR;
|
waiting_response = CMD_WRITE_BD_ADDR_RSP;
|
|
hci_ev = ble_transport_alloc_evt(0);
|
if (hci_ev) {
|
hci_ev->opcode = BLE_HCI_EVCODE_COMMAND_COMPLETE;
|
hci_ev->length = sizeof(*ev);
|
ev = (void *) hci_ev->data;
|
|
ev->num_packets = 1;
|
ev->opcode = waiting_opcode;
|
ev->status = 0;
|
ble_hci_edtt_cmdevt_tx((uint8_t *) hci_ev, BLE_HCI_EDTT_EVT);
|
}
|
}
|
|
/* Reads and executes EDTT commands. */
|
static void
|
edtt_poller(void *arg) {
|
uint16_t command;
|
uint16_t size;
|
uint16_t opcode;
|
uint8_t bdaddr[6];
|
|
/* Initialize HCI command opcode and response variables */
|
waiting_opcode = 0;
|
waiting_response = CMD_NOTHING;
|
edtt_q_event_count = 0;
|
|
/* Initialize and start EDTT system */
|
enable_edtt_mode();
|
set_edtt_autoshutdown(true);
|
edtt_start();
|
|
#if EDTT_HCI_LOGS
|
log_hci_init();
|
#endif
|
|
while (1) {
|
edtt_read((uint8_t *) &command, sizeof(command), EDTTT_BLOCK);
|
edtt_read((uint8_t *) &size, sizeof(size), EDTTT_BLOCK);
|
|
bs_trace_raw_time(4, "command 0x%04X received (size %u) "
|
"events=%u\n",
|
command, size, edtt_q_event_count);
|
|
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;
|
case CMD_WRITE_BD_ADDR_REQ:
|
edtt_read((uint8_t *) &opcode, sizeof(opcode), EDTTT_BLOCK);
|
|
if (opcode == BT_HCI_OP_VS_WRITE_BD_ADDR) {
|
edtt_read((uint8_t *) &bdaddr, sizeof(bdaddr), EDTTT_BLOCK);
|
ble_ll_set_public_addr(bdaddr);
|
fake_write_bd_addr_cc();
|
} else {
|
assert(0);
|
}
|
break;
|
default:
|
if (size >= 2) {
|
edtt_read((uint8_t *) &opcode, sizeof(opcode), EDTTT_BLOCK);
|
send_hci_cmd_to_ctrl(opcode, size - 2, command + 1);
|
}
|
}
|
}
|
}
|
|
int
|
edtt_init(void)
|
{
|
os_stack_t dummy_stack;
|
int rc;
|
|
rc = os_task_init(&edtt_poller_task, "edttpoll", edtt_poller, NULL,
|
MYNEWT_VAL(EDTT_POLLER_PRIO), OS_WAIT_FOREVER,
|
&dummy_stack, 1);
|
assert(rc == 0);
|
|
rc = os_task_init(&edtt_service_task, "edttsvc", service_events, NULL,
|
MYNEWT_VAL(EDTT_POLLER_PRIO) + 1, OS_WAIT_FOREVER,
|
&dummy_stack, 1);
|
assert(rc == 0);
|
|
return 0;
|
}
|
|
/**
|
* Initializes the EDTT HCI transport module.
|
*
|
* @return 0 on success;
|
* A BLE_ERR_[...] error code on failure.
|
*/
|
void
|
ble_hci_edtt_init(void)
|
{
|
/* Ensure this function only gets called by sysinit. */
|
SYSINIT_ASSERT_ACTIVE();
|
|
os_eventq_init(&edtt_q_svc);
|
os_eventq_init(&edtt_q_event);
|
os_eventq_init(&edtt_q_data);
|
}
|