Fork ESP-IDF's bluetooth component

i want better sbc encoding, and no cla will stop me
This commit is contained in:
jacqueline
2024-03-28 14:32:49 +11:00
parent 239e6d8950
commit ee29c25b29
1761 changed files with 737738 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
#include "hci/hci_audio.h"
void set_audio_state(uint16_t handle, sco_codec_t codec, sco_state_t state)
{
//todo
return;
}
+652
View File
@@ -0,0 +1,652 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#include <string.h>
#include "common/bt_defs.h"
#include "common/bt_trace.h"
#include "stack/bt_types.h"
#include "hci/hci_hal.h"
#include "hci/hci_internals.h"
#include "hci/hci_layer.h"
#include "hci/hci_trans_int.h"
#include "osi/thread.h"
#include "osi/pkt_queue.h"
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
#include "osi/mutex.h"
#include "osi/alarm.h"
#endif
#if (BT_CONTROLLER_INCLUDED == TRUE)
#include "esp_bt.h"
#endif
#include "esp_bluedroid_hci.h"
#include "stack/hcimsgs.h"
#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER)
#include "nimble/ble_hci_trans.h"
#endif
#if (C2H_FLOW_CONTROL_INCLUDED == TRUE)
#include "l2c_int.h"
#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE
#include "stack/hcimsgs.h"
#define HCI_BLE_EVENT 0x3e
#define PACKET_TYPE_TO_INBOUND_INDEX(type) ((type) - 2)
#define PACKET_TYPE_TO_INDEX(type) ((type) - 1)
#define HCI_UPSTREAM_DATA_QUEUE_IDX (1)
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
#define HCI_BLE_ADV_MIN_CREDITS_TO_RELEASE (10)
#define HCI_ADV_FLOW_MONITOR_PERIOD_MS (500)
#else
#define HCI_HAL_BLE_ADV_RPT_QUEUE_LEN_MAX (200)
#endif
extern bool BTU_check_queue_is_congest(void);
static const uint8_t preamble_sizes[] = {
HCI_COMMAND_PREAMBLE_SIZE,
HCI_ACL_PREAMBLE_SIZE,
HCI_SCO_PREAMBLE_SIZE,
HCI_EVENT_PREAMBLE_SIZE
};
static const uint16_t outbound_event_types[] = {
MSG_HC_TO_STACK_HCI_ERR,
MSG_HC_TO_STACK_HCI_ACL,
MSG_HC_TO_STACK_HCI_SCO,
MSG_HC_TO_STACK_HCI_EVT
};
typedef struct {
fixed_queue_t *rx_q;
struct pkt_queue *adv_rpt_q;
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
osi_mutex_t adv_flow_lock;
osi_alarm_t *adv_flow_monitor;
int adv_credits;
int adv_credits_to_release;
pkt_linked_item_t *adv_fc_cmd_buf;
bool cmd_buf_in_use;
#endif
hci_hal_callbacks_t *callbacks;
osi_thread_t *hci_h4_thread;
struct osi_event *upstream_data_ready;
} hci_hal_env_t;
static hci_hal_env_t hci_hal_env;
static const hci_hal_t interface;
static const esp_bluedroid_hci_driver_callbacks_t hci_host_cb;
static void host_send_pkt_available_cb(void);
static int host_recv_pkt_cb(uint8_t *data, uint16_t len);
static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet);
static void hci_hal_h4_hdl_rx_adv_rpt(pkt_linked_item_t *linked_pkt);
static void hci_upstream_data_handler(void *arg);
static bool hci_upstream_data_post(uint32_t timeout);
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
static void hci_adv_flow_monitor(void *context);
static void hci_adv_flow_cmd_free_cb(pkt_linked_item_t *linked_pkt);
#endif
static bool hci_hal_env_init(const hci_hal_callbacks_t *upper_callbacks, osi_thread_t *task_thread)
{
assert(upper_callbacks != NULL);
assert(task_thread != NULL);
hci_hal_env.hci_h4_thread = task_thread;
hci_hal_env.callbacks = (hci_hal_callbacks_t *)upper_callbacks;
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
hci_hal_env.adv_fc_cmd_buf = osi_calloc(HCI_CMD_LINKED_BUF_SIZE(HCIC_PARAM_SIZE_BLE_UPDATE_ADV_FLOW_CONTROL));
assert(hci_hal_env.adv_fc_cmd_buf != NULL);
osi_mutex_new(&hci_hal_env.adv_flow_lock);
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
hci_hal_env.adv_credits = BLE_ADV_REPORT_FLOW_CONTROL_NUM;
hci_hal_env.adv_credits_to_release = 0;
hci_hal_env.cmd_buf_in_use = false;
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
hci_hal_env.adv_flow_monitor = osi_alarm_new("adv_fc_mon", hci_adv_flow_monitor, NULL, HCI_ADV_FLOW_MONITOR_PERIOD_MS);
assert (hci_hal_env.adv_flow_monitor != NULL);
#endif
hci_hal_env.rx_q = fixed_queue_new(QUEUE_SIZE_MAX);
assert(hci_hal_env.rx_q != NULL);
hci_hal_env.adv_rpt_q = pkt_queue_create();
assert(hci_hal_env.adv_rpt_q != NULL);
struct osi_event *event = osi_event_create(hci_upstream_data_handler, NULL);
assert(event != NULL);
hci_hal_env.upstream_data_ready = event;
osi_event_bind(hci_hal_env.upstream_data_ready, hci_hal_env.hci_h4_thread, HCI_UPSTREAM_DATA_QUEUE_IDX);
return true;
}
static void hci_hal_env_deinit(void)
{
fixed_queue_t *rx_q = hci_hal_env.rx_q;
struct pkt_queue *adv_rpt_q = hci_hal_env.adv_rpt_q;
struct osi_event *upstream_data_ready = hci_hal_env.upstream_data_ready;
hci_hal_env.rx_q = NULL;
hci_hal_env.adv_rpt_q = NULL;
hci_hal_env.upstream_data_ready = NULL;
fixed_queue_free(rx_q, osi_free_func);
pkt_queue_destroy(adv_rpt_q, NULL);
osi_event_delete(upstream_data_ready);
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
hci_hal_env.cmd_buf_in_use = true;
osi_alarm_cancel(hci_hal_env.adv_flow_monitor);
osi_alarm_free(hci_hal_env.adv_flow_monitor);
hci_hal_env.adv_flow_monitor = NULL;
osi_mutex_free(&hci_hal_env.adv_flow_lock);
osi_free(hci_hal_env.adv_fc_cmd_buf);
hci_hal_env.adv_fc_cmd_buf = NULL;
#endif
hci_hal_env.hci_h4_thread = NULL;
memset(&hci_hal_env, 0, sizeof(hci_hal_env_t));
}
static bool hal_open(const hci_hal_callbacks_t *upper_callbacks, void *task_thread)
{
hci_hal_env_init(upper_callbacks, (osi_thread_t *)task_thread);
//register vhci host cb
if (hci_host_register_callback(&hci_host_cb) != ESP_OK) {
return false;
}
return true;
}
static void hal_close(void)
{
hci_hal_env_deinit();
}
/**
* Function: transmit_data -TX data to low-layer
* It is ported from Bluedroid source code, so it is not
* needed to use write() to send data.
* TODO: Just use firmware API to send data.
*/
static uint16_t transmit_data(serial_data_type_t type,
uint8_t *data, uint16_t length)
{
uint8_t previous_byte;
assert(data != NULL);
assert(length > 0);
if (type < DATA_TYPE_COMMAND || type > DATA_TYPE_SCO) {
HCI_TRACE_ERROR("%s invalid data type: %d", __func__, type);
return 0;
}
// Write the signal byte right before the data
--data;
previous_byte = *data;
*(data) = type;
++length;
BTTRC_DUMP_BUFFER("Transmit Pkt", data, length);
// TX Data to target
hci_host_send_packet(data, length);
// Be nice and restore the old value of that byte
*(data) = previous_byte;
return length - 1;
}
// Internal functions
static void hci_upstream_data_handler(void *arg)
{
fixed_queue_t *rx_q = hci_hal_env.rx_q;
struct pkt_queue *adv_rpt_q = hci_hal_env.adv_rpt_q;
size_t pkts_to_process;
do {
pkts_to_process = fixed_queue_length(rx_q);
for (size_t i = 0; i < pkts_to_process; i++) {
BT_HDR *packet = fixed_queue_dequeue(rx_q, 0);
if (packet != NULL) {
hci_hal_h4_hdl_rx_packet(packet);
}
}
} while (0);
do {
pkts_to_process = pkt_queue_length(adv_rpt_q);
for (size_t i = 0; i < pkts_to_process; i++) {
pkt_linked_item_t *linked_pkt = pkt_queue_dequeue(adv_rpt_q);
if (linked_pkt != NULL) {
hci_hal_h4_hdl_rx_adv_rpt(linked_pkt);
}
}
} while (0);
if (!fixed_queue_is_empty(rx_q) || pkt_queue_length(adv_rpt_q) > 0) {
hci_upstream_data_post(OSI_THREAD_MAX_TIMEOUT);
}
}
static bool hci_upstream_data_post(uint32_t timeout)
{
return osi_thread_post_event(hci_hal_env.upstream_data_ready, timeout);
}
#if (C2H_FLOW_CONTROL_INCLUDED == TRUE)
static void hci_packet_complete(BT_HDR *packet){
uint8_t type;
uint16_t handle;
uint16_t num_packets = 1;
uint8_t *stream = packet->data + packet->offset;
STREAM_TO_UINT8(type, stream);
if (type == DATA_TYPE_ACL/* || type == DATA_TYPE_SCO*/) {
STREAM_TO_UINT16(handle, stream);
handle = handle & HCI_DATA_HANDLE_MASK;
btsnd_hcic_host_num_xmitted_pkts(1, &handle, &num_packets);
}
}
#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE
bool host_recv_adv_packet(uint8_t *packet)
{
assert(packet);
if(packet[0] == DATA_TYPE_EVENT && packet[1] == HCI_BLE_EVENT) {
if(packet[3] == HCI_BLE_ADV_PKT_RPT_EVT || packet[3] == HCI_BLE_DIRECT_ADV_EVT
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|| packet[3] == HCI_BLE_ADV_DISCARD_REPORT_EVT
#endif
) {
return true;
}
}
return false;
}
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
static void hci_adv_flow_monitor(void *context)
{
hci_adv_credits_force_release(0);
}
static void hci_adv_credits_consumed(uint16_t num)
{
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
assert(hci_hal_env.adv_credits >= num);
hci_hal_env.adv_credits -= num;
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
}
int hci_adv_credits_prep_to_release(uint16_t num)
{
if (num == 0) {
return hci_hal_env.adv_credits_to_release;
}
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
int credits_to_release = hci_hal_env.adv_credits_to_release + num;
assert(hci_hal_env.adv_credits_to_release <= BLE_ADV_REPORT_FLOW_CONTROL_NUM);
hci_hal_env.adv_credits_to_release = credits_to_release;
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
if (credits_to_release == num && num != 0) {
osi_alarm_cancel(hci_hal_env.adv_flow_monitor);
osi_alarm_set(hci_hal_env.adv_flow_monitor, HCI_ADV_FLOW_MONITOR_PERIOD_MS);
}
return credits_to_release;
}
static int hci_adv_credits_release(void)
{
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
int credits_released = hci_hal_env.adv_credits_to_release;
hci_hal_env.adv_credits += credits_released;
hci_hal_env.adv_credits_to_release -= credits_released;
assert(hci_hal_env.adv_credits <= BLE_ADV_REPORT_FLOW_CONTROL_NUM);
assert(hci_hal_env.adv_credits_to_release >= 0);
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
if (hci_hal_env.adv_credits_to_release == 0) {
osi_alarm_cancel(hci_hal_env.adv_flow_monitor);
}
return credits_released;
}
static int hci_adv_credits_release_rollback(uint16_t num)
{
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
hci_hal_env.adv_credits -= num;
hci_hal_env.adv_credits_to_release += num;
assert(hci_hal_env.adv_credits >=0);
assert(hci_hal_env.adv_credits_to_release <= BLE_ADV_REPORT_FLOW_CONTROL_NUM);
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
return num;
}
static void hci_adv_flow_cmd_free_cb(pkt_linked_item_t *linked_pkt)
{
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
hci_hal_env.cmd_buf_in_use = false;
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
hci_adv_credits_try_release(0);
}
bool hci_adv_flow_try_send_command(uint16_t credits_released)
{
bool sent = false;
bool use_static_buffer = false;
/* first try using static buffer, then dynamic buffer */
if (!hci_hal_env.cmd_buf_in_use) {
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
if (!hci_hal_env.cmd_buf_in_use) {
hci_hal_env.cmd_buf_in_use = true;
use_static_buffer = true;
}
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
}
if (use_static_buffer) {
hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(hci_hal_env.adv_fc_cmd_buf->data);
BT_HDR *static_buffer = &metadata->command;
metadata->command_free_cb = hci_adv_flow_cmd_free_cb;
sent = btsnd_hcic_ble_update_adv_report_flow_control(credits_released, static_buffer);
} else {
sent = btsnd_hcic_ble_update_adv_report_flow_control(credits_released, NULL);
}
return sent;
}
int hci_adv_credits_try_release(uint16_t num)
{
int credits_released = 0;
if (hci_adv_credits_prep_to_release(num) >= HCI_BLE_ADV_MIN_CREDITS_TO_RELEASE) {
credits_released = hci_adv_credits_release();
if (credits_released > 0) {
if (!hci_adv_flow_try_send_command(credits_released)) {
hci_adv_credits_release_rollback(credits_released);
}
} else {
assert (credits_released == 0);
}
}
return credits_released;
}
int hci_adv_credits_force_release(uint16_t num)
{
hci_adv_credits_prep_to_release(num);
int credits_released = hci_adv_credits_release();
if (credits_released > 0) {
if (!hci_adv_flow_try_send_command(credits_released)) {
hci_adv_credits_release_rollback(credits_released);
}
}
return credits_released;
}
#endif
static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet)
{
uint8_t type, hdr_size;
uint16_t length;
uint8_t *stream = NULL;
if (!packet) {
return;
}
stream = packet->data + packet->offset;
#if (C2H_FLOW_CONTROL_INCLUDED == TRUE)
hci_packet_complete(packet);
#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE
STREAM_TO_UINT8(type, stream);
packet->offset++;
packet->len--;
if (type == HCI_BLE_EVENT) {
#if (!CONFIG_BT_STACK_NO_LOG)
uint8_t len = 0;
STREAM_TO_UINT8(len, stream);
#endif
HCI_TRACE_ERROR("Workround stream corrupted during LE SCAN: pkt_len=%d ble_event_len=%d\n",
packet->len, len);
osi_free(packet);
return;
}
if (type < DATA_TYPE_ACL || type > DATA_TYPE_EVENT) {
HCI_TRACE_ERROR("%s Unknown HCI message type. Dropping this byte 0x%x,"
" min %x, max %x\n", __func__, type,
DATA_TYPE_ACL, DATA_TYPE_EVENT);
osi_free(packet);
return;
}
hdr_size = preamble_sizes[type - 1];
if (packet->len < hdr_size) {
HCI_TRACE_ERROR("Wrong packet length type=%d pkt_len=%d hdr_len=%d",
type, packet->len, hdr_size);
osi_free(packet);
return;
}
if (type == DATA_TYPE_ACL) {
stream += hdr_size - 2;
STREAM_TO_UINT16(length, stream);
} else {
stream += hdr_size - 1;
STREAM_TO_UINT8(length, stream);
}
if ((length + hdr_size) != packet->len) {
HCI_TRACE_ERROR("Wrong packet length type=%d hdr_len=%d pd_len=%d "
"pkt_len=%d", type, hdr_size, length, packet->len);
osi_free(packet);
return;
}
packet->event = outbound_event_types[PACKET_TYPE_TO_INDEX(type)];
hci_hal_env.callbacks->packet_ready(packet);
}
static void hci_hal_h4_hdl_rx_adv_rpt(pkt_linked_item_t *linked_pkt)
{
uint8_t type;
uint8_t hdr_size;
uint16_t length;
uint8_t *stream = NULL;
if (!linked_pkt) {
return;
}
BT_HDR* packet = (BT_HDR *)linked_pkt->data;
stream = packet->data + packet->offset;
assert(host_recv_adv_packet(stream) == true);
STREAM_TO_UINT8(type, stream);
packet->offset++;
packet->len--;
hdr_size = preamble_sizes[type - 1];
if (packet->len < hdr_size) {
HCI_TRACE_ERROR("Wrong packet length type=%d pkt_len=%d hdr_len=%d",
type, packet->len, hdr_size);
goto _discard_packet;
}
stream += hdr_size - 1;
STREAM_TO_UINT8(length, stream);
if ((length + hdr_size) != packet->len) {
HCI_TRACE_ERROR("Wrong packet length type=%d hdr_len=%d pd_len=%d "
"pkt_len=%d", type, hdr_size, length, packet->len);
goto _discard_packet;
}
#if SCAN_QUEUE_CONGEST_CHECK
if(BTU_check_queue_is_congest()) {
HCI_TRACE_DEBUG("BtuQueue is congested");
goto _discard_packet;
}
#endif
packet->event = outbound_event_types[PACKET_TYPE_TO_INDEX(type)];
hci_hal_env.callbacks->adv_rpt_ready(linked_pkt);
return;
_discard_packet:
osi_free(linked_pkt);
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
hci_adv_credits_prep_to_release(1);
#endif
}
static void host_send_pkt_available_cb(void)
{
//Controller rx cache buffer is ready for receiving new host packet
//Just Call Host main thread task to process pending packets.
hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT);
}
static int host_recv_pkt_cb(uint8_t *data, uint16_t len)
{
//Target has packet to host, malloc new buffer for packet
BT_HDR *pkt = NULL;
pkt_linked_item_t *linked_pkt = NULL;
size_t pkt_size;
if (hci_hal_env.rx_q == NULL) {
return 0;
}
bool is_adv_rpt = host_recv_adv_packet(data);
if (!is_adv_rpt) {
pkt_size = BT_HDR_SIZE + len;
pkt = (BT_HDR *) osi_calloc(pkt_size);
if (!pkt) {
HCI_TRACE_ERROR("%s couldn't aquire memory for inbound data buffer.\n", __func__);
assert(0);
}
pkt->offset = 0;
pkt->len = len;
pkt->layer_specific = 0;
memcpy(pkt->data, data, len);
fixed_queue_enqueue(hci_hal_env.rx_q, pkt, FIXED_QUEUE_MAX_TIMEOUT);
} else {
#if !BLE_ADV_REPORT_FLOW_CONTROL
// drop the packets if pkt_queue length goes beyond upper limit
if (pkt_queue_length(hci_hal_env.adv_rpt_q) > HCI_HAL_BLE_ADV_RPT_QUEUE_LEN_MAX) {
return 0;
}
#endif
pkt_size = BT_PKT_LINKED_HDR_SIZE + BT_HDR_SIZE + len;
linked_pkt = (pkt_linked_item_t *) osi_calloc(pkt_size);
if (!linked_pkt) {
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
hci_adv_credits_consumed(1);
hci_adv_credits_prep_to_release(1);
#endif
return 0;
}
pkt = (BT_HDR *)linked_pkt->data;
pkt->offset = 0;
pkt->len = len;
pkt->layer_specific = 0;
memcpy(pkt->data, data, len);
pkt_queue_enqueue(hci_hal_env.adv_rpt_q, linked_pkt);
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
hci_adv_credits_consumed(1);
#endif
}
hci_upstream_data_post(OSI_THREAD_MAX_TIMEOUT);
BTTRC_DUMP_BUFFER("Recv Pkt", pkt->data, len);
return 0;
}
#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER)
int
ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg)
{
if(esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) {
ble_hci_trans_buf_free(hci_ev);
return 0;
}
uint16_t len = hci_ev[1] + 3;
uint8_t *data = (uint8_t *)malloc(len);
assert(data != NULL);
data[0] = 0x04;
memcpy(&data[1], hci_ev, len - 1);
ble_hci_trans_buf_free(hci_ev);
host_recv_pkt_cb(data, len);
free(data);
return 0;
}
int
ble_hs_rx_data(struct os_mbuf *om, void *arg)
{
uint16_t len = OS_MBUF_PKTHDR(om)->omp_len + 1;
uint8_t *data = (uint8_t *)malloc(len);
assert(data != NULL);
data[0] = 0x02;
os_mbuf_copydata(om, 0, len - 1, &data[1]);
host_recv_pkt_cb(data, len);
free(data);
os_mbuf_free_chain(om);
return 0;
}
#endif
static const esp_bluedroid_hci_driver_callbacks_t hci_host_cb = {
.notify_host_send_available = host_send_pkt_available_cb,
.notify_host_recv = host_recv_pkt_cb,
};
static const hci_hal_t interface = {
hal_open,
hal_close,
transmit_data,
};
const hci_hal_t *hci_hal_h4_get_interface(void)
{
return &interface;
}
+610
View File
@@ -0,0 +1,610 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#include <string.h>
#include "sdkconfig.h"
#include "common/bt_target.h"
#if (BT_CONTROLLER_INCLUDED == TRUE)
#include "esp_bt.h"
#endif
#include "common/bt_defs.h"
#include "common/bt_trace.h"
#include "stack/hcidefs.h"
#include "stack/hcimsgs.h"
#include "stack/btu.h"
#include "common/bt_vendor_lib.h"
#include "hci/hci_internals.h"
#include "hci/hci_hal.h"
#include "hci/hci_layer.h"
#include "hci/hci_trans_int.h"
#include "osi/allocator.h"
#include "hci/packet_fragmenter.h"
#include "osi/list.h"
#include "osi/alarm.h"
#include "osi/thread.h"
#include "osi/mutex.h"
#include "osi/fixed_queue.h"
#include "osi/fixed_pkt_queue.h"
#define HCI_HOST_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
#define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE)
#define HCI_HOST_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 3)
#define HCI_HOST_TASK_NAME "hciT"
#define HCI_HOST_TASK_WORKQUEUE_NUM (2)
#define HCI_HOST_TASK_WORKQUEUE0_LEN (1) // for downstream datapath
#define HCI_HOST_TASK_WORKQUEUE1_LEN (1) // for upstream datapath
#define HCI_DOWNSTREAM_DATA_QUEUE_IDX (0)
typedef struct {
bool timer_is_set;
osi_alarm_t *command_response_timer;
list_t *commands_pending_response;
osi_mutex_t commands_pending_response_lock;
} command_waiting_response_t;
typedef struct {
int command_credits;
fixed_pkt_queue_t *command_queue;
fixed_queue_t *packet_queue;
struct osi_event *downstream_data_ready;
command_waiting_response_t cmd_waiting_q;
/*
non_repeating_timer_t *command_response_timer;
list_t *commands_pending_response;
osi_mutex_t commands_pending_response_lock;
*/
} hci_host_env_t;
// Using a define here, because it can be stringified for the property lookup
static const uint32_t COMMAND_PENDING_TIMEOUT = 8000;
// Our interface
static bool interface_created;
static hci_t interface;
static hci_host_env_t hci_host_env;
static osi_thread_t *hci_host_thread;
static bool hci_host_startup_flag;
// Modules we import and callbacks we export
static const hci_hal_t *hal;
static const hci_hal_callbacks_t hal_callbacks;
static const packet_fragmenter_t *packet_fragmenter;
static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks;
static int hci_layer_init_env(void);
static void hci_layer_deinit_env(void);
static void hci_downstream_data_handler(void *arg);
static void event_command_ready(fixed_pkt_queue_t *queue);
static void event_packet_ready(fixed_queue_t *queue);
static void restart_command_waiting_response_timer(command_waiting_response_t *cmd_wait_q);
static void command_timed_out(void *context);
static void hal_says_packet_ready(BT_HDR *packet);
static bool filter_incoming_event(BT_HDR *packet);
static serial_data_type_t event_to_data_type(uint16_t event);
static pkt_linked_item_t *get_waiting_command(command_opcode_t opcode);
static void dispatch_reassembled(BT_HDR *packet);
static void dispatch_adv_report(pkt_linked_item_t *linked_pkt);
// Module lifecycle functions
int hci_start_up(void)
{
if (hci_layer_init_env()) {
goto error;
}
const size_t workqueue_len[] = {HCI_HOST_TASK_WORKQUEUE0_LEN, HCI_HOST_TASK_WORKQUEUE1_LEN};
hci_host_thread = osi_thread_create(HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, HCI_HOST_TASK_PRIO, HCI_HOST_TASK_PINNED_TO_CORE,
HCI_HOST_TASK_WORKQUEUE_NUM, workqueue_len);
if (hci_host_thread == NULL) {
return -2;
}
osi_event_bind(hci_host_env.downstream_data_ready, hci_host_thread, HCI_DOWNSTREAM_DATA_QUEUE_IDX);
packet_fragmenter->init(&packet_fragmenter_callbacks);
hal->open(&hal_callbacks, hci_host_thread);
hci_host_startup_flag = true;
return 0;
error:
hci_shut_down();
return -1;
}
void hci_shut_down(void)
{
hci_host_startup_flag = false;
hci_layer_deinit_env();
packet_fragmenter->cleanup();
//low_power_manager->cleanup();
hal->close();
osi_thread_free(hci_host_thread);
hci_host_thread = NULL;
}
bool hci_downstream_data_post(uint32_t timeout)
{
return osi_thread_post_event(hci_host_env.downstream_data_ready, timeout);
}
static int hci_layer_init_env(void)
{
command_waiting_response_t *cmd_wait_q;
// The host is only allowed to send at most one command initially,
// as per the Bluetooth spec, Volume 2, Part E, 4.4 (Command Flow Control)
// This value can change when you get a command complete or command status event.
hci_host_env.command_credits = 1;
hci_host_env.command_queue = fixed_pkt_queue_new(QUEUE_SIZE_MAX);
if (hci_host_env.command_queue) {
fixed_pkt_queue_register_dequeue(hci_host_env.command_queue, event_command_ready);
} else {
HCI_TRACE_ERROR("%s unable to create pending command queue.", __func__);
return -1;
}
struct osi_event *event = osi_event_create(hci_downstream_data_handler, NULL);
assert(event != NULL);
hci_host_env.downstream_data_ready = event;
hci_host_env.packet_queue = fixed_queue_new(QUEUE_SIZE_MAX);
if (hci_host_env.packet_queue) {
fixed_queue_register_dequeue(hci_host_env.packet_queue, event_packet_ready);
} else {
HCI_TRACE_ERROR("%s unable to create pending packet queue.", __func__);
return -1;
}
// Init Commands waiting response list and timer
cmd_wait_q = &hci_host_env.cmd_waiting_q;
cmd_wait_q->timer_is_set = false;
cmd_wait_q->commands_pending_response = list_new(NULL);
if (!cmd_wait_q->commands_pending_response) {
HCI_TRACE_ERROR("%s unable to create list for commands pending response.", __func__);
return -1;
}
osi_mutex_new(&cmd_wait_q->commands_pending_response_lock);
cmd_wait_q->command_response_timer = osi_alarm_new("cmd_rsp_to", command_timed_out, cmd_wait_q, COMMAND_PENDING_TIMEOUT);
if (!cmd_wait_q->command_response_timer) {
HCI_TRACE_ERROR("%s unable to create command response timer.", __func__);
return -1;
}
#if (BLE_50_FEATURE_SUPPORT == TRUE)
btsnd_hcic_ble_sync_sem_init();
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
return 0;
}
static void hci_layer_deinit_env(void)
{
command_waiting_response_t *cmd_wait_q;
osi_event_delete(hci_host_env.downstream_data_ready);
hci_host_env.downstream_data_ready = NULL;
if (hci_host_env.command_queue) {
fixed_pkt_queue_free(hci_host_env.command_queue, (fixed_pkt_queue_free_cb)osi_free_func);
}
if (hci_host_env.packet_queue) {
fixed_queue_free(hci_host_env.packet_queue, osi_free_func);
}
cmd_wait_q = &hci_host_env.cmd_waiting_q;
list_free(cmd_wait_q->commands_pending_response);
osi_mutex_free(&cmd_wait_q->commands_pending_response_lock);
osi_alarm_free(cmd_wait_q->command_response_timer);
cmd_wait_q->command_response_timer = NULL;
#if (BLE_50_FEATURE_SUPPORT == TRUE)
btsnd_hcic_ble_sync_sem_deinit();
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
}
static void hci_downstream_data_handler(void *arg)
{
/*
* Previous task handles RX queue and two TX Queues, Since there is
* a RX Thread Task in H4 layer which receives packet from driver layer.
* Now HCI Host Task has been optimized to only process TX Queue
* including command and data queue. And command queue has high priority,
* All packets will be directly copied to single queue in driver layer with
* H4 type header added (1 byte).
*/
while (hci_host_check_send_available()) {
/*Now Target only allowed one packet per TX*/
BT_HDR *pkt = packet_fragmenter->fragment_current_packet();
if (pkt != NULL) {
packet_fragmenter->fragment_and_dispatch(pkt);
} else if (!fixed_pkt_queue_is_empty(hci_host_env.command_queue) &&
hci_host_env.command_credits > 0) {
fixed_pkt_queue_process(hci_host_env.command_queue);
} else if (!fixed_queue_is_empty(hci_host_env.packet_queue)) {
fixed_queue_process(hci_host_env.packet_queue);
} else {
// No downstream packet to send, stop processing
break;
}
}
}
static void transmit_command(
BT_HDR *command,
command_complete_cb complete_callback,
command_status_cb status_callback,
void *context)
{
hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(command);
pkt_linked_item_t *linked_pkt = HCI_GET_CMD_LINKED_STRUCT(metadata);
assert(command->layer_specific == HCI_CMD_BUF_TYPE_METADATA);
metadata->flags_vnd |= HCI_CMD_MSG_F_VND_QUEUED;
// Store the command message type in the event field
// in case the upper layer didn't already
command->event = MSG_STACK_TO_HC_HCI_CMD;
HCI_TRACE_DEBUG("HCI Enqueue Comamnd opcode=0x%x\n", metadata->opcode);
BTTRC_DUMP_BUFFER(NULL, command->data + command->offset, command->len);
fixed_pkt_queue_enqueue(hci_host_env.command_queue, linked_pkt, FIXED_PKT_QUEUE_MAX_TIMEOUT);
hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT);
}
static future_t *transmit_command_futured(BT_HDR *command)
{
hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(command);
pkt_linked_item_t *linked_pkt = HCI_GET_CMD_LINKED_STRUCT(metadata);
assert(command->layer_specific == HCI_CMD_BUF_TYPE_METADATA);
metadata->flags_vnd |= (HCI_CMD_MSG_F_VND_QUEUED | HCI_CMD_MSG_F_VND_FUTURE);
future_t *future = future_new();
metadata->complete_future = future;
// Store the command message type in the event field
// in case the upper layer didn't already
command->event = MSG_STACK_TO_HC_HCI_CMD;
fixed_pkt_queue_enqueue(hci_host_env.command_queue, linked_pkt, FIXED_PKT_QUEUE_MAX_TIMEOUT);
hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT);
return future;
}
static void transmit_downward(uint16_t type, void *data)
{
if (type == MSG_STACK_TO_HC_HCI_CMD) {
HCI_TRACE_ERROR("%s legacy transmit of command. Use transmit_command instead.\n", __func__);
assert(0);
} else {
fixed_queue_enqueue(hci_host_env.packet_queue, data, FIXED_QUEUE_MAX_TIMEOUT);
}
hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT);
}
// Command/packet transmitting functions
static void event_command_ready(fixed_pkt_queue_t *queue)
{
pkt_linked_item_t *wait_entry = NULL;
command_waiting_response_t *cmd_wait_q = &hci_host_env.cmd_waiting_q;
wait_entry = fixed_pkt_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT);
hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(wait_entry->data);
metadata->flags_vnd |= HCI_CMD_MSG_F_VND_SENT;
metadata->flags_vnd &= ~HCI_CMD_MSG_F_VND_QUEUED;
if (metadata->flags_src & HCI_CMD_MSG_F_SRC_NOACK) {
packet_fragmenter->fragment_and_dispatch(&metadata->command);
hci_cmd_free_cb free_func = metadata->command_free_cb ? metadata->command_free_cb : (hci_cmd_free_cb) osi_free_func;
free_func(wait_entry);
return;
}
hci_host_env.command_credits--;
// Move it to the list of commands awaiting response
osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT);
list_append(cmd_wait_q->commands_pending_response, wait_entry);
osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock);
// Send it off
packet_fragmenter->fragment_and_dispatch(&metadata->command);
restart_command_waiting_response_timer(cmd_wait_q);
}
static void event_packet_ready(fixed_queue_t *queue)
{
BT_HDR *packet = (BT_HDR *)fixed_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT);
// The queue may be the command queue or the packet queue, we don't care
packet_fragmenter->fragment_and_dispatch(packet);
}
// Callback for the fragmenter to send a fragment
static void transmit_fragment(BT_HDR *packet, bool send_transmit_finished)
{
uint16_t event = packet->event & MSG_EVT_MASK;
serial_data_type_t type = event_to_data_type(event);
hal->transmit_data(type, packet->data + packet->offset, packet->len);
if (event != MSG_STACK_TO_HC_HCI_CMD && send_transmit_finished) {
osi_free(packet);
}
}
static void fragmenter_transmit_finished(BT_HDR *packet, bool all_fragments_sent)
{
if (all_fragments_sent) {
osi_free(packet);
} else {
// This is kind of a weird case, since we're dispatching a partially sent packet
// up to a higher layer.
// TODO(zachoverflow): rework upper layer so this isn't necessary.
//osi_free(packet);
/* dispatch_reassembled(packet) will send the packet back to the higher layer
when controller buffer is not enough. hci will send the remain packet back
to the l2cap layer and saved in the Link Queue (p_lcb->link_xmit_data_q).
The l2cap layer will resend the packet to lower layer when controller buffer
can be used.
*/
dispatch_reassembled(packet);
//data_dispatcher_dispatch(interface.event_dispatcher, packet->event & MSG_EVT_MASK, packet);
}
}
static void restart_command_waiting_response_timer(command_waiting_response_t *cmd_wait_q)
{
osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT);
if (cmd_wait_q->timer_is_set) {
osi_alarm_cancel(cmd_wait_q->command_response_timer);
cmd_wait_q->timer_is_set = false;
}
if (!list_is_empty(cmd_wait_q->commands_pending_response)) {
osi_alarm_set(cmd_wait_q->command_response_timer, COMMAND_PENDING_TIMEOUT);
cmd_wait_q->timer_is_set = true;
}
osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock);
}
static void command_timed_out(void *context)
{
command_waiting_response_t *cmd_wait_q = (command_waiting_response_t *)context;
pkt_linked_item_t *wait_entry;
osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT);
wait_entry = (list_is_empty(cmd_wait_q->commands_pending_response) ?
NULL : list_front(cmd_wait_q->commands_pending_response));
osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock);
if (wait_entry == NULL) {
HCI_TRACE_ERROR("%s with no commands pending response", __func__);
} else
// We shouldn't try to recover the stack from this command timeout.
// If it's caused by a software bug, fix it. If it's a hardware bug, fix it.
{
hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(wait_entry->data);
HCI_TRACE_ERROR("%s hci layer timeout waiting for response to a command. opcode: 0x%x", __func__, metadata->opcode);
UNUSED(metadata);
}
}
// Event/packet receiving functions
static void hal_says_packet_ready(BT_HDR *packet)
{
if (packet->event != MSG_HC_TO_STACK_HCI_EVT) {
packet_fragmenter->reassemble_and_dispatch(packet);
} else if (!filter_incoming_event(packet)) {
dispatch_reassembled(packet);
}
}
static void hal_says_adv_rpt_ready(pkt_linked_item_t *linked_pkt)
{
dispatch_adv_report(linked_pkt);
}
// Returns true if the event was intercepted and should not proceed to
// higher layers. Also inspects an incoming event for interesting
// information, like how many commands are now able to be sent.
static bool filter_incoming_event(BT_HDR *packet)
{
pkt_linked_item_t *wait_entry = NULL;
hci_cmd_metadata_t *metadata = NULL;
uint8_t *stream = packet->data + packet->offset;
uint8_t event_code;
command_opcode_t opcode;
STREAM_TO_UINT8(event_code, stream);
STREAM_SKIP_UINT8(stream); // Skip the parameter total length field
HCI_TRACE_DEBUG("Receive packet event_code=0x%x\n", event_code);
if (event_code == HCI_COMMAND_COMPLETE_EVT) {
STREAM_TO_UINT8(hci_host_env.command_credits, stream);
STREAM_TO_UINT16(opcode, stream);
wait_entry = get_waiting_command(opcode);
metadata = (hci_cmd_metadata_t *)(wait_entry->data);
if (!wait_entry) {
HCI_TRACE_WARNING("%s command complete event with no matching command. opcode: 0x%x.", __func__, opcode);
} else if (metadata->command_complete_cb) {
metadata->command_complete_cb(packet, metadata->context);
#if (BLE_50_FEATURE_SUPPORT == TRUE)
BlE_SYNC *sync_info = btsnd_hcic_ble_get_sync_info();
if(!sync_info) {
HCI_TRACE_WARNING("%s sync_info is NULL. opcode = 0x%x", __func__, opcode);
} else {
if (sync_info->sync_sem && sync_info->opcode == opcode) {
osi_sem_give(&sync_info->sync_sem);
sync_info->opcode = 0;
}
}
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
} else if (metadata->flags_vnd & HCI_CMD_MSG_F_VND_FUTURE) {
future_ready((future_t *)(metadata->complete_future), packet);
}
goto intercepted;
} else if (event_code == HCI_COMMAND_STATUS_EVT) {
uint8_t status;
STREAM_TO_UINT8(status, stream);
STREAM_TO_UINT8(hci_host_env.command_credits, stream);
STREAM_TO_UINT16(opcode, stream);
// If a command generates a command status event, it won't be getting a command complete event
wait_entry = get_waiting_command(opcode);
metadata = (hci_cmd_metadata_t *)(wait_entry->data);
if (!wait_entry) {
HCI_TRACE_WARNING("%s command status event with no matching command. opcode: 0x%x", __func__, opcode);
} else if (metadata->command_status_cb) {
metadata->command_status_cb(status, &metadata->command, metadata->context);
}
goto intercepted;
}
return false;
intercepted:
restart_command_waiting_response_timer(&hci_host_env.cmd_waiting_q);
/*Tell HCI Host Task to continue TX Pending commands*/
if (hci_host_env.command_credits &&
!fixed_pkt_queue_is_empty(hci_host_env.command_queue)) {
hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT);
}
if (wait_entry) {
// If it has a callback, it's responsible for freeing the packet
if (event_code == HCI_COMMAND_STATUS_EVT ||
(!metadata->command_complete_cb && !metadata->complete_future)) {
osi_free(packet);
}
// If it has a callback, it's responsible for freeing the command
if (event_code == HCI_COMMAND_COMPLETE_EVT || !metadata->command_status_cb) {
hci_cmd_free_cb free_func = metadata->command_free_cb ? metadata->command_free_cb : (hci_cmd_free_cb) osi_free_func;
free_func(wait_entry);
}
} else {
osi_free(packet);
}
return true;
}
// Callback for the fragmenter to dispatch up a completely reassembled packet
static void dispatch_reassembled(BT_HDR *packet)
{
// Events should already have been dispatched before this point
//Tell Up-layer received packet.
if (btu_task_post(SIG_BTU_HCI_MSG, packet, OSI_THREAD_MAX_TIMEOUT) == false) {
osi_free(packet);
}
}
static void dispatch_adv_report(pkt_linked_item_t *linked_pkt)
{
// Events should already have been dispatched before this point
//Tell Up-layer received packet.
if (btu_task_post(SIG_BTU_HCI_ADV_RPT_MSG, linked_pkt, OSI_THREAD_MAX_TIMEOUT) == false) {
osi_free(linked_pkt);
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
hci_adv_credits_try_release(1);
#endif
}
}
// Misc internal functions
// TODO(zachoverflow): we seem to do this a couple places, like the HCI inject module. #centralize
static serial_data_type_t event_to_data_type(uint16_t event)
{
if (event == MSG_STACK_TO_HC_HCI_ACL) {
return DATA_TYPE_ACL;
} else if (event == MSG_STACK_TO_HC_HCI_SCO) {
return DATA_TYPE_SCO;
} else if (event == MSG_STACK_TO_HC_HCI_CMD) {
return DATA_TYPE_COMMAND;
} else {
HCI_TRACE_ERROR("%s invalid event type, could not translate 0x%x\n", __func__, event);
}
return 0;
}
static pkt_linked_item_t *get_waiting_command(command_opcode_t opcode)
{
command_waiting_response_t *cmd_wait_q = &hci_host_env.cmd_waiting_q;
osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT);
for (const list_node_t *node = list_begin(cmd_wait_q->commands_pending_response);
node != list_end(cmd_wait_q->commands_pending_response);
node = list_next(node)) {
pkt_linked_item_t *wait_entry = list_node(node);
if (wait_entry) {
hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(wait_entry->data);
if (metadata->opcode == opcode) {
list_remove(cmd_wait_q->commands_pending_response, wait_entry);
osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock);
return wait_entry;
}
}
}
osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock);
return NULL;
}
static void init_layer_interface(void)
{
if (!interface_created) {
interface.transmit_command = transmit_command;
interface.transmit_command_futured = transmit_command_futured;
interface.transmit_downward = transmit_downward;
interface_created = true;
}
}
static const hci_hal_callbacks_t hal_callbacks = {
hal_says_packet_ready,
hal_says_adv_rpt_ready,
};
static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks = {
transmit_fragment,
dispatch_reassembled,
fragmenter_transmit_finished
};
const hci_t *hci_layer_get_interface(void)
{
hal = hci_hal_h4_get_interface();
packet_fragmenter = packet_fragmenter_get_interface();
init_layer_interface();
return &interface;
}
@@ -0,0 +1,284 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#include "common/bt_defs.h"
#include "osi/allocator.h"
#include "stack/bt_types.h"
#include "stack/hcidefs.h"
#include "stack/hcimsgs.h"
#include "hci/hci_internals.h"
#include "hci/hci_layer.h"
#include "hci/hci_packet_factory.h"
static BT_HDR *make_command_no_params(uint16_t opcode);
static BT_HDR *make_command(uint16_t opcode, size_t parameter_size, uint8_t **stream_out);
// Interface functions
static BT_HDR *make_reset(void)
{
return make_command_no_params(HCI_RESET);
}
static BT_HDR *make_read_buffer_size(void)
{
return make_command_no_params(HCI_READ_BUFFER_SIZE);
}
static BT_HDR *make_set_c2h_flow_control(uint8_t enable)
{
uint8_t *stream;
const uint8_t parameter_size = 1;
BT_HDR *packet = make_command(HCI_SET_HC_TO_HOST_FLOW_CTRL, parameter_size, &stream);
UINT8_TO_STREAM(stream, enable);
return packet;
}
static BT_HDR *make_set_adv_report_flow_control(uint8_t enable, uint16_t num, uint16_t lost_threshold)
{
uint8_t *stream;
const uint8_t parameter_size = 1 + 2 + 2;
BT_HDR *packet = make_command(HCI_VENDOR_BLE_SET_ADV_FLOW_CONTROL, parameter_size, &stream);
UINT8_TO_STREAM(stream, enable);
UINT16_TO_STREAM(stream, num);
UINT16_TO_STREAM(stream, lost_threshold);
return packet;
}
static BT_HDR *make_host_buffer_size(uint16_t acl_size, uint8_t sco_size, uint16_t acl_count, uint16_t sco_count)
{
uint8_t *stream;
const uint8_t parameter_size = 2 + 1 + 2 + 2; // from each of the parameters
BT_HDR *packet = make_command(HCI_HOST_BUFFER_SIZE, parameter_size, &stream);
UINT16_TO_STREAM(stream, acl_size);
UINT8_TO_STREAM(stream, sco_size);
UINT16_TO_STREAM(stream, acl_count);
UINT16_TO_STREAM(stream, sco_count);
return packet;
}
static BT_HDR *make_read_local_version_info(void)
{
return make_command_no_params(HCI_READ_LOCAL_VERSION_INFO);
}
static BT_HDR *make_read_bd_addr(void)
{
return make_command_no_params(HCI_READ_BD_ADDR);
}
static BT_HDR *make_read_local_supported_commands(void)
{
return make_command_no_params(HCI_READ_LOCAL_SUPPORTED_CMDS);
}
static BT_HDR *make_read_local_supported_features(void)
{
return make_command_no_params(HCI_READ_LOCAL_FEATURES);
}
static BT_HDR *make_read_local_extended_features(uint8_t page_number)
{
uint8_t *stream;
const uint8_t parameter_size = 1;
BT_HDR *packet = make_command(HCI_READ_LOCAL_EXT_FEATURES, parameter_size, &stream);
UINT8_TO_STREAM(stream, page_number);
return packet;
}
static BT_HDR *make_write_simple_pairing_mode(uint8_t mode)
{
uint8_t *stream;
const uint8_t parameter_size = 1;
BT_HDR *packet = make_command(HCI_WRITE_SIMPLE_PAIRING_MODE, parameter_size, &stream);
UINT8_TO_STREAM(stream, mode);
return packet;
}
static BT_HDR *make_write_secure_connections_host_support(uint8_t mode)
{
uint8_t *stream;
const uint8_t parameter_size = 1;
BT_HDR *packet = make_command(HCI_WRITE_SECURE_CONNS_SUPPORT, parameter_size, &stream);
UINT8_TO_STREAM(stream, mode);
return packet;
}
static BT_HDR *make_set_event_mask(const bt_event_mask_t *event_mask)
{
uint8_t *stream;
uint8_t parameter_size = sizeof(bt_event_mask_t);
BT_HDR *packet = make_command(HCI_SET_EVENT_MASK, parameter_size, &stream);
ARRAY8_TO_STREAM(stream, event_mask->as_array);
return packet;
}
static BT_HDR *make_ble_write_host_support(uint8_t supported_host, uint8_t simultaneous_host)
{
uint8_t *stream;
const uint8_t parameter_size = 1 + 1;
BT_HDR *packet = make_command(HCI_WRITE_LE_HOST_SUPPORT, parameter_size, &stream);
UINT8_TO_STREAM(stream, supported_host);
UINT8_TO_STREAM(stream, simultaneous_host);
return packet;
}
static BT_HDR *make_ble_read_white_list_size(void)
{
return make_command_no_params(HCI_BLE_READ_WHITE_LIST_SIZE);
}
static BT_HDR *make_ble_read_buffer_size(void)
{
return make_command_no_params(HCI_BLE_READ_BUFFER_SIZE);
}
static BT_HDR *make_ble_read_supported_states(void)
{
return make_command_no_params(HCI_BLE_READ_SUPPORTED_STATES);
}
static BT_HDR *make_ble_read_local_supported_features(void)
{
return make_command_no_params(HCI_BLE_READ_LOCAL_SPT_FEAT);
}
static BT_HDR *make_ble_read_resolving_list_size(void)
{
return make_command_no_params(HCI_BLE_READ_RESOLVING_LIST_SIZE);
}
static BT_HDR *make_ble_read_suggested_default_data_length(void)
{
return make_command_no_params(HCI_BLE_READ_DEFAULT_DATA_LENGTH);
}
static BT_HDR *make_ble_write_suggested_default_data_length(uint16_t SuggestedMaxTxOctets, uint16_t SuggestedMaxTxTime)
{
uint8_t *stream;
uint8_t parameter_size = sizeof(uint16_t) + sizeof(uint16_t);
BT_HDR *packet = make_command(HCI_BLE_WRITE_DEFAULT_DATA_LENGTH, parameter_size, &stream);
UINT16_TO_STREAM(stream, SuggestedMaxTxOctets);
UINT16_TO_STREAM(stream, SuggestedMaxTxTime);
return packet;
}
static BT_HDR *make_ble_set_event_mask(const bt_event_mask_t *event_mask)
{
uint8_t *stream;
uint8_t parameter_size = sizeof(bt_event_mask_t);
BT_HDR *packet = make_command(HCI_BLE_SET_EVENT_MASK, parameter_size, &stream);
ARRAY8_TO_STREAM(stream, event_mask->as_array);
return packet;
}
static BT_HDR *make_write_sync_flow_control_enable(uint8_t enable)
{
uint8_t *stream;
const uint8_t parameter_size = 1;
BT_HDR *packet = make_command(HCI_WRITE_SCO_FLOW_CTRL_ENABLE, parameter_size, &stream);
UINT8_TO_STREAM(stream, enable);
return packet;
}
static BT_HDR *make_write_default_erroneous_data_report(uint8_t enable)
{
uint8_t *stream;
const uint8_t parameter_size = 1;
BT_HDR *packet = make_command(HCI_WRITE_ERRONEOUS_DATA_RPT, parameter_size, &stream);
UINT8_TO_STREAM(stream, enable);
return packet;
}
#if (BLE_50_FEATURE_SUPPORT == TRUE)
static BT_HDR *make_read_max_adv_data_len(void)
{
return make_command_no_params(HCI_BLE_RD_MAX_ADV_DATA_LEN);
}
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
// Internal functions
static BT_HDR *make_command_no_params(uint16_t opcode)
{
return make_command(opcode, 0, NULL);
}
static BT_HDR *make_command(uint16_t opcode, size_t parameter_size, uint8_t **stream_out)
{
BT_HDR *packet = HCI_GET_CMD_BUF(parameter_size);
hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(packet);
metadata->opcode = opcode;
uint8_t *stream = packet->data;
UINT16_TO_STREAM(stream, opcode);
UINT8_TO_STREAM(stream, parameter_size);
if (stream_out != NULL) {
*stream_out = stream;
}
return packet;
}
static const hci_packet_factory_t interface = {
make_reset,
make_read_buffer_size,
make_set_c2h_flow_control,
make_set_adv_report_flow_control,
make_host_buffer_size,
make_read_local_version_info,
make_read_bd_addr,
make_read_local_supported_commands,
make_read_local_supported_features,
make_read_local_extended_features,
make_write_simple_pairing_mode,
make_write_secure_connections_host_support,
make_set_event_mask,
make_ble_write_host_support,
make_ble_read_white_list_size,
make_ble_read_buffer_size,
make_ble_read_supported_states,
make_ble_read_local_supported_features,
make_ble_read_resolving_list_size,
#if (BLE_50_FEATURE_SUPPORT == TRUE)
make_read_max_adv_data_len,
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
make_ble_read_suggested_default_data_length,
make_ble_write_suggested_default_data_length,
make_ble_set_event_mask,
make_write_sync_flow_control_enable,
make_write_default_erroneous_data_report,
};
const hci_packet_factory_t *hci_packet_factory_get_interface(void)
{
return &interface;
}
@@ -0,0 +1,285 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#include "common/bt_defs.h"
#include "stack/bt_types.h"
#include "stack/hcimsgs.h"
#include "hci/hci_layer.h"
#include "hci/hci_packet_parser.h"
static const command_opcode_t NO_OPCODE_CHECKING = 0;
static uint8_t *read_command_complete_header(
BT_HDR *response,
command_opcode_t expected_opcode,
size_t minimum_bytes_after);
static void parse_generic_command_complete(BT_HDR *response)
{
read_command_complete_header(response, NO_OPCODE_CHECKING, 0 /* bytes after */);
osi_free(response);
}
static void parse_read_buffer_size_response(
BT_HDR *response,
uint16_t *acl_data_size_ptr,
uint16_t *acl_buffer_count_ptr,
uint8_t *sco_data_size_ptr,
uint16_t *sco_buffer_count_ptr)
{
uint8_t *stream = read_command_complete_header(response, HCI_READ_BUFFER_SIZE, 7 /* bytes after */);
assert(stream != NULL);
STREAM_TO_UINT16(*acl_data_size_ptr, stream);
STREAM_TO_UINT8(*sco_data_size_ptr, stream);
STREAM_TO_UINT16(*acl_buffer_count_ptr, stream);
STREAM_TO_UINT16(*sco_buffer_count_ptr, stream);
osi_free(response);
}
static void parse_read_local_version_info_response(
BT_HDR *response,
bt_version_t *bt_version)
{
uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_VERSION_INFO, 8 /* bytes after */);
assert(stream != NULL);
STREAM_TO_UINT8(bt_version->hci_version, stream);
STREAM_TO_UINT16(bt_version->hci_revision, stream);
STREAM_TO_UINT8(bt_version->lmp_version, stream);
STREAM_TO_UINT16(bt_version->manufacturer, stream);
STREAM_TO_UINT16(bt_version->lmp_subversion, stream);
osi_free(response);
}
static void parse_read_bd_addr_response(
BT_HDR *response,
bt_bdaddr_t *address_ptr)
{
uint8_t *stream = read_command_complete_header(response, HCI_READ_BD_ADDR, sizeof(bt_bdaddr_t) /* bytes after */);
assert(stream != NULL);
STREAM_TO_BDADDR(address_ptr->address, stream);
osi_free(response);
}
static void parse_read_local_supported_commands_response(
BT_HDR *response,
uint8_t *supported_commands_ptr,
size_t supported_commands_length)
{
uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_SUPPORTED_CMDS, supported_commands_length /* bytes after */);
assert(stream != NULL);
STREAM_TO_ARRAY(supported_commands_ptr, stream, (int)supported_commands_length);
osi_free(response);
}
static void parse_read_local_supported_features_response(
BT_HDR *response,
bt_device_features_t *feature_pages)
{
uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_FEATURES, sizeof(bt_device_features_t) /* bytes after */);
if (stream != NULL) {
STREAM_TO_ARRAY(feature_pages->as_array, stream, (int)sizeof(bt_device_features_t));
}
osi_free(response);
}
static void parse_read_local_extended_features_response(
BT_HDR *response,
uint8_t *page_number_ptr,
uint8_t *max_page_number_ptr,
bt_device_features_t *feature_pages,
size_t feature_pages_count)
{
uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_EXT_FEATURES, 2 + sizeof(bt_device_features_t) /* bytes after */);
if (stream != NULL) {
STREAM_TO_UINT8(*page_number_ptr, stream);
STREAM_TO_UINT8(*max_page_number_ptr, stream);
assert(*page_number_ptr < feature_pages_count);
STREAM_TO_ARRAY(feature_pages[*page_number_ptr].as_array, stream, (int)sizeof(bt_device_features_t));
} else {
HCI_TRACE_ERROR("%s() - WARNING: READING EXTENDED FEATURES FAILED. "
"THIS MAY INDICATE A FIRMWARE/CONTROLLER ISSUE.", __func__);
}
osi_free(response);
}
static void parse_ble_read_white_list_size_response(
BT_HDR *response,
uint8_t *white_list_size_ptr)
{
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_WHITE_LIST_SIZE, 1 /* byte after */);
assert(stream != NULL);
STREAM_TO_UINT8(*white_list_size_ptr, stream);
osi_free(response);
}
static void parse_ble_read_buffer_size_response(
BT_HDR *response,
uint16_t *data_size_ptr,
uint8_t *acl_buffer_count_ptr)
{
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_BUFFER_SIZE, 3 /* bytes after */);
assert(stream != NULL);
STREAM_TO_UINT16(*data_size_ptr, stream);
STREAM_TO_UINT8(*acl_buffer_count_ptr, stream);
osi_free(response);
}
static void parse_ble_read_supported_states_response(
BT_HDR *response,
uint8_t *supported_states,
size_t supported_states_size)
{
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_SUPPORTED_STATES, supported_states_size /* bytes after */);
assert(stream != NULL);
STREAM_TO_ARRAY(supported_states, stream, (int)supported_states_size);
osi_free(response);
}
static void parse_ble_read_local_supported_features_response(
BT_HDR *response,
bt_device_features_t *supported_features)
{
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_LOCAL_SPT_FEAT, sizeof(bt_device_features_t) /* bytes after */);
assert(stream != NULL);
STREAM_TO_ARRAY(supported_features->as_array, stream, (int)sizeof(bt_device_features_t));
osi_free(response);
}
static void parse_ble_read_resolving_list_size_response(
BT_HDR *response,
uint8_t *resolving_list_size_ptr)
{
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_RESOLVING_LIST_SIZE, 1 /* bytes after */);
STREAM_TO_UINT8(*resolving_list_size_ptr, stream);
osi_free(response);
}
static void parse_ble_read_suggested_default_data_length_response(
BT_HDR *response,
uint16_t *ble_default_packet_length_ptr,
uint16_t *ble_default_packet_txtime_ptr)
{
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_DEFAULT_DATA_LENGTH, 2 /* bytes after */);
STREAM_TO_UINT16(*ble_default_packet_length_ptr, stream);
STREAM_TO_UINT16(*ble_default_packet_txtime_ptr, stream);
osi_free(response);
}
#if (BLE_50_FEATURE_SUPPORT == TRUE)
static void parse_ble_read_adv_max_len_response(
BT_HDR *response,
uint16_t *adv_max_len_ptr)
{
uint8_t *stream = read_command_complete_header(response, HCI_BLE_RD_MAX_ADV_DATA_LEN, 1 /* bytes after */);
// Size: 2 Octets ; Value: 0x001F 0x0672 ; Maximum supported advertising data length
STREAM_TO_UINT16(*adv_max_len_ptr, stream);
osi_free(response);
}
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
// Internal functions
static uint8_t *read_command_complete_header(
BT_HDR *response,
command_opcode_t expected_opcode,
size_t minimum_bytes_after)
{
uint8_t *stream = response->data + response->offset;
// Read the event header
uint8_t event_code __attribute__((unused));
uint8_t parameter_length __attribute__((unused));
STREAM_TO_UINT8(event_code, stream);
STREAM_TO_UINT8(parameter_length, stream);
const size_t parameter_bytes_we_read_here __attribute__((unused)) = 4;
// Check the event header values against what we expect
assert(event_code == HCI_COMMAND_COMPLETE_EVT);
assert(parameter_length >= (parameter_bytes_we_read_here + minimum_bytes_after));
// Read the command complete header
command_opcode_t opcode __attribute__((unused));
uint8_t status;
STREAM_SKIP_UINT8(stream); // skip the number of hci command packets field
STREAM_TO_UINT16(opcode, stream);
// Check the command complete header values against what we expect
if (expected_opcode != NO_OPCODE_CHECKING) {
assert(opcode == expected_opcode);
}
// Assume the next field is the status field
STREAM_TO_UINT8(status, stream);
if (status != HCI_SUCCESS) {
return NULL;
}
return stream;
}
static const hci_packet_parser_t interface = {
parse_generic_command_complete,
parse_read_buffer_size_response,
parse_read_local_version_info_response,
parse_read_bd_addr_response,
parse_read_local_supported_commands_response,
parse_read_local_supported_features_response,
parse_read_local_extended_features_response,
parse_ble_read_white_list_size_response,
parse_ble_read_buffer_size_response,
parse_ble_read_supported_states_response,
parse_ble_read_local_supported_features_response,
parse_ble_read_resolving_list_size_response,
#if (BLE_50_FEATURE_SUPPORT == TRUE)
parse_ble_read_adv_max_len_response,
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
parse_ble_read_suggested_default_data_length_response
};
const hci_packet_parser_t *hci_packet_parser_get_interface(void)
{
return &interface;
}
@@ -0,0 +1,361 @@
/******************************************************************************
*
* Copyright (C) 2009-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#ifndef BT_VENDOR_LIB_H
#define BT_VENDOR_LIB_H
#include <stdint.h>
//#include <sys/cdefs.h>
//#include <sys/types.h>
/** Struct types */
/** Typedefs and defines */
/** Vendor specific operations OPCODE */
typedef enum {
/* [operation]
* Power on or off the BT Controller.
* [input param]
* A pointer to int type with content of bt_vendor_power_state_t.
* Typecasting conversion: (int *) param.
* [return]
* 0 - default, don't care.
* [callback]
* None.
*/
BT_VND_OP_POWER_CTRL,
/* [operation]
* Perform any vendor specific initialization or configuration
* on the BT Controller. This is called before stack initialization.
* [input param]
* None.
* [return]
* 0 - default, don't care.
* [callback]
* Must call fwcfg_cb to notify the stack of the completion of vendor
* specific initialization once it has been done.
*/
BT_VND_OP_FW_CFG,
/* [operation]
* Perform any vendor specific SCO/PCM configuration on the BT Controller.
* This is called after stack initialization.
* [input param]
* None.
* [return]
* 0 - default, don't care.
* [callback]
* Must call scocfg_cb to notify the stack of the completion of vendor
* specific SCO configuration once it has been done.
*/
BT_VND_OP_SCO_CFG,
/* [operation]
* Open UART port on where the BT Controller is attached.
* This is called before stack initialization.
* [input param]
* A pointer to int array type for open file descriptors.
* The mapping of HCI channel to fd slot in the int array is given in
* bt_vendor_hci_channels_t.
* And, it requires the vendor lib to fill up the content before returning
* the call.
* Typecasting conversion: (int (*)[]) param.
* [return]
* Numbers of opened file descriptors.
* Valid number:
* 1 - CMD/EVT/ACL-In/ACL-Out via the same fd (e.g. UART)
* 2 - CMD/EVT on one fd, and ACL-In/ACL-Out on the other fd
* 4 - CMD, EVT, ACL-In, ACL-Out are on their individual fd
* [callback]
* None.
*/
BT_VND_OP_USERIAL_OPEN,
/* [operation]
* Close the previously opened UART port.
* [input param]
* None.
* [return]
* 0 - default, don't care.
* [callback]
* None.
*/
BT_VND_OP_USERIAL_CLOSE,
/* [operation]
* Get the LPM idle timeout in milliseconds.
* The stack uses this information to launch a timer delay before it
* attempts to de-assert LPM WAKE signal once downstream HCI packet
* has been delivered.
* [input param]
* A pointer to uint32_t type which is passed in by the stack. And, it
* requires the vendor lib to fill up the content before returning
* the call.
* Typecasting conversion: (uint32_t *) param.
* [return]
* 0 - default, don't care.
* [callback]
* None.
*/
BT_VND_OP_GET_LPM_IDLE_TIMEOUT,
/* [operation]
* Enable or disable LPM mode on BT Controller.
* [input param]
* A pointer to uint8_t type with content of bt_vendor_lpm_mode_t.
* Typecasting conversion: (uint8_t *) param.
* [return]
* 0 - default, don't care.
* [callback]
* Must call lpm_cb to notify the stack of the completion of LPM
* disable/enable process once it has been done.
*/
BT_VND_OP_LPM_SET_MODE,
/* [operation]
* Assert or Deassert LPM WAKE on BT Controller.
* [input param]
* A pointer to uint8_t type with content of bt_vendor_lpm_wake_state_t.
* Typecasting conversion: (uint8_t *) param.
* [return]
* 0 - default, don't care.
* [callback]
* None.
*/
BT_VND_OP_LPM_WAKE_SET_STATE,
/* [operation]
* Perform any vendor specific commands related to audio state changes.
* [input param]
* a pointer to bt_vendor_op_audio_state_t indicating what audio state is
* set.
* [return]
* 0 - default, don't care.
* [callback]
* None.
*/
BT_VND_OP_SET_AUDIO_STATE,
/* [operation]
* The epilog call to the vendor module so that it can perform any
* vendor-specific processes (e.g. send a HCI_RESET to BT Controller)
* before the caller calls for cleanup().
* [input param]
* None.
* [return]
* 0 - default, don't care.
* [callback]
* Must call epilog_cb to notify the stack of the completion of vendor
* specific epilog process once it has been done.
*/
BT_VND_OP_EPILOG,
} bt_vendor_opcode_t;
/** Power on/off control states */
typedef enum {
BT_VND_PWR_OFF,
BT_VND_PWR_ON,
} bt_vendor_power_state_t;
/** Define HCI channel identifier in the file descriptors array
used in BT_VND_OP_USERIAL_OPEN operation.
*/
typedef enum {
CH_CMD, // HCI Command channel
CH_EVT, // HCI Event channel
CH_ACL_OUT, // HCI ACL downstream channel
CH_ACL_IN, // HCI ACL upstream channel
CH_MAX // Total channels
} bt_vendor_hci_channels_t;
/** LPM disable/enable request */
typedef enum {
BT_VND_LPM_DISABLE,
BT_VND_LPM_ENABLE,
} bt_vendor_lpm_mode_t;
/** LPM WAKE set state request */
typedef enum {
BT_VND_LPM_WAKE_ASSERT,
BT_VND_LPM_WAKE_DEASSERT,
} bt_vendor_lpm_wake_state_t;
/** Callback result values */
typedef enum {
BT_VND_OP_RESULT_SUCCESS,
BT_VND_OP_RESULT_FAIL,
} bt_vendor_op_result_t;
/** audio (SCO) state changes triggering VS commands for configuration */
typedef struct {
uint16_t handle;
uint16_t peer_codec;
uint16_t state;
} bt_vendor_op_audio_state_t;
/*
* Bluetooth Host/Controller Vendor callback structure.
*/
/* vendor initialization/configuration callback */
typedef void (*cfg_result_cb)(bt_vendor_op_result_t result);
/* datapath buffer allocation callback (callout)
*
* Vendor lib needs to request a buffer through the alloc callout function
* from HCI lib if the buffer is for constructing a HCI Command packet which
* will be sent through xmit_cb to BT Controller.
*
* For each buffer allocation, the requested size needs to be big enough to
* accommodate the below header plus a complete HCI packet --
* typedef struct
* {
* uint16_t event;
* uint16_t len;
* uint16_t offset;
* uint16_t layer_specific;
* } HC_BT_HDR;
*
* HCI lib returns a pointer to the buffer where Vendor lib should use to
* construct a HCI command packet as below format:
*
* --------------------------------------------
* | HC_BT_HDR | HCI command |
* --------------------------------------------
* where
* HC_BT_HDR.event = 0x2000;
* HC_BT_HDR.len = Length of HCI command;
* HC_BT_HDR.offset = 0;
* HC_BT_HDR.layer_specific = 0;
*
* For example, a HCI_RESET Command will be formed as
* ------------------------
* | HC_BT_HDR |03|0c|00|
* ------------------------
* with
* HC_BT_HDR.event = 0x2000;
* HC_BT_HDR.len = 3;
* HC_BT_HDR.offset = 0;
* HC_BT_HDR.layer_specific = 0;
*/
typedef void *(*malloc_cb)(int size);
/* datapath buffer deallocation callback (callout) */
typedef void (*mdealloc_cb)(void *p_buf);
/* define callback of the cmd_xmit_cb
*
* The callback function which HCI lib will call with the return of command
* complete packet. Vendor lib is responsible for releasing the buffer passed
* in at the p_mem parameter by calling dealloc callout function.
*/
typedef void (*tINT_CMD_CBACK)(void *p_mem);
/* hci command packet transmit callback (callout)
*
* Vendor lib calls xmit_cb callout function in order to send a HCI Command
* packet to BT Controller. The buffer carrying HCI Command packet content
* needs to be first allocated through the alloc callout function.
* HCI lib will release the buffer for Vendor lib once it has delivered the
* packet content to BT Controller.
*
* Vendor lib needs also provide a callback function (p_cback) which HCI lib
* will call with the return of command complete packet.
*
* The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of
* HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command
* packet.
*/
typedef uint8_t (*cmd_xmit_cb)(uint16_t opcode, void *p_buf, tINT_CMD_CBACK p_cback);
typedef struct {
/** set to sizeof(bt_vendor_callbacks_t) */
size_t size;
/*
* Callback and callout functions have implemented in HCI libray
* (libbt-hci.so).
*/
/* notifies caller result of firmware configuration request */
cfg_result_cb fwcfg_cb;
/* notifies caller result of sco configuration request */
cfg_result_cb scocfg_cb;
/* notifies caller result of lpm enable/disable */
cfg_result_cb lpm_cb;
/* notifies the result of codec setting */
cfg_result_cb audio_state_cb;
/* buffer allocation request */
malloc_cb alloc;
/* buffer deallocation request */
mdealloc_cb dealloc;
/* hci command packet transmit request */
cmd_xmit_cb xmit_cb;
/* notifies caller completion of epilog process */
cfg_result_cb epilog_cb;
} bt_vendor_callbacks_t;
/*
* Bluetooth Host/Controller VENDOR Interface
*/
typedef struct {
/** Set to sizeof(bt_vndor_interface_t) */
size_t size;
/*
* Functions need to be implemented in Vendor libray (libbt-vendor.so).
*/
/**
* Caller will open the interface and pass in the callback routines
* to the implemenation of this interface.
*/
int (*init)(const bt_vendor_callbacks_t *p_cb, unsigned char *local_bdaddr);
/** Vendor specific operations */
int (*op)(bt_vendor_opcode_t opcode, void *param);
/** Closes the interface */
void (*cleanup)(void);
} bt_vendor_interface_t;
/*
* External shared lib functions/data
*/
/* Entry point of DLib --
* Vendor library needs to implement the body of bt_vendor_interface_t
* structure and uses the below name as the variable name. HCI library
* will use this symbol name to get address of the object through the
* dlsym call.
*/
//extern const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE;
#endif /* BT_VENDOR_LIB_H */
@@ -0,0 +1,42 @@
/******************************************************************************
*
* Copyright (C) 2015 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#ifndef _HCI_AUDIO_H_
#define _HCI_AUDIO_H_
#include <stdint.h>
// Audio state definitions.
typedef enum {
SCO_STATE_OFF = 0, // Audio is off.
SCO_STATE_OFF_TRANSFER, // Closed pending final transfer of audio.
SCO_STATE_ON, // Audio is on.
SCO_STATE_SETUP, // Open pending completion of audio setup.
} sco_state_t;
// Codec type definitions.
typedef enum {
SCO_CODEC_NONE = 0x0000,
SCO_CODEC_CVSD = 0x0001,
SCO_CODEC_MSBC = 0x0002,
} sco_codec_t;
// Set the audio state on the controller for SCO (PCM, WBS, ...) using the
// vendor library.
void set_audio_state(uint16_t handle, sco_codec_t codec, sco_state_t state);
#endif /* _HCI_AUDIO_H_ */
@@ -0,0 +1,96 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#ifndef _HCI_HAL_H_
#define _HCI_HAL_H_
#include <stdbool.h>
#include <stdint.h>
#include "common/bt_target.h"
#include "osi/pkt_queue.h"
#include "stack/bt_types.h"
#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER)
#include "os/os_mbuf.h"
#endif
typedef enum {
DATA_TYPE_COMMAND = 1,
DATA_TYPE_ACL = 2,
DATA_TYPE_SCO = 3,
DATA_TYPE_EVENT = 4
} serial_data_type_t;
typedef void (*packet_ready_cb)(BT_HDR *packet);
typedef void (*adv_rpt_ready_cb)(pkt_linked_item_t *linked_pkt);
typedef struct {
// Called when the HAL detects inbound data.
// Data |type| may be ACL, SCO, or EVENT.
// Executes in the context of the thread supplied to |init|.
packet_ready_cb packet_ready;
adv_rpt_ready_cb adv_rpt_ready;
/*
// Called when the HAL detects inbound astronauts named Dave.
// HAL will deny all requests to open the pod bay doors after this.
dave_ready_cb dave_ready;
*/
} hci_hal_callbacks_t;
typedef struct hci_hal_t {
// Initialize the HAL, with |upper_callbacks| and |upper_thread| to run in the context of.
//bool (*init)(const hci_hal_callbacks_t *upper_callbacks);
// Connect to the underlying hardware, and let data start flowing.
bool (*open)(const hci_hal_callbacks_t *upper_callbacks, void *task_thread);
// Disconnect from the underlying hardware, and close the HAL.
// "Daisy, Daisy..."
void (*close)(void);
// Retrieve up to |max_size| bytes for ACL, SCO, or EVENT data packets into
// |buffer|, blocking until max_size bytes read if |block| is true.
// Only guaranteed to be correct in the context of a data_ready callback
// of the corresponding type.
//size_t (*read_data)(serial_data_type_t type, uint8_t *buffer, size_t max_size);
// The upper layer must call this to notify the HAL that it has finished
// reading a packet of the specified |type|. Underlying implementations that
// use shared channels for multiple data types depend on this to know when
// to reinterpret the data stream.
//void (*packet_finished)(serial_data_type_t type);
// Transmit COMMAND, ACL, or SCO data packets.
// |data| may not be NULL. |length| must be greater than zero.
//
// IMPORTANT NOTE:
// Depending on the underlying implementation, the byte right
// before the beginning of |data| may be borrowed during this call
// and then restored to its original value.
// This is safe in the bluetooth context, because there is always a buffer
// header that prefixes data you're sending.
uint16_t (*transmit_data)(serial_data_type_t type, uint8_t *data, uint16_t length);
} hci_hal_t;
// Gets the correct hal implementation, as compiled for.
const hci_hal_t *hci_hal_h4_get_interface(void);
#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER)
int ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg);
int ble_hs_rx_data(struct os_mbuf *om, void *arg);
#endif
#endif /* _HCI_HAL_H */
@@ -0,0 +1,31 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#ifndef _HCI_INTERNALS_H_
#define _HCI_INTERNALS_H_
// 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
#define HCI_COMMAND_PREAMBLE_SIZE 3
// 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2)
#define HCI_ACL_PREAMBLE_SIZE 4
// 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3)
#define HCI_SCO_PREAMBLE_SIZE 3
// 1 byte for event code, 1 byte for parameter length (Volume 2, Part E, 5.4.4)
#define HCI_EVENT_PREAMBLE_SIZE 2
#endif /* _HCI_INTERNALS_H_ */
@@ -0,0 +1,113 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#ifndef _HCI_LAYER_H_
#define _HCI_LAYER_H_
#include "common/bt_target.h"
#include "stack/bt_types.h"
#include "osi/allocator.h"
#include "osi/osi.h"
#include "osi/future.h"
#include "osi/thread.h"
#include "osi/pkt_queue.h"
///// LEGACY DEFINITIONS /////
/* Message event mask across Host/Controller lib and stack */
#define MSG_EVT_MASK 0xFF00 /* eq. BT_EVT_MASK */
#define MSG_SUB_EVT_MASK 0x00FF /* eq. BT_SUB_EVT_MASK */
/* Message event ID passed from Host/Controller lib to stack */
#define MSG_HC_TO_STACK_HCI_ERR 0x1300 /* eq. BT_EVT_TO_BTU_HCIT_ERR */
#define MSG_HC_TO_STACK_HCI_ACL 0x1100 /* eq. BT_EVT_TO_BTU_HCI_ACL */
#define MSG_HC_TO_STACK_HCI_SCO 0x1200 /* eq. BT_EVT_TO_BTU_HCI_SCO */
#define MSG_HC_TO_STACK_HCI_EVT 0x1000 /* eq. BT_EVT_TO_BTU_HCI_EVT */
#define MSG_HC_TO_STACK_L2C_SEG_XMIT 0x1900 /* eq. BT_EVT_TO_BTU_L2C_SEG_XMIT */
/* Message event ID passed from stack to vendor lib */
#define MSG_STACK_TO_HC_HCI_ACL 0x2100 /* eq. BT_EVT_TO_LM_HCI_ACL */
#define MSG_STACK_TO_HC_HCI_SCO 0x2200 /* eq. BT_EVT_TO_LM_HCI_SCO */
#define MSG_STACK_TO_HC_HCI_CMD 0x2000 /* eq. BT_EVT_TO_LM_HCI_CMD */
/* Local Bluetooth Controller ID for BR/EDR */
#define LOCAL_BR_EDR_CONTROLLER_ID 0
#define HCI_CMD_MSG_F_VND_FUTURE (0x01)
#define HCI_CMD_MSG_F_VND_QUEUED (0x02)
#define HCI_CMD_MSG_F_VND_SENT (0x04)
///// END LEGACY DEFINITIONS /////
typedef struct hci_hal_t hci_hal_t;
//typedef struct btsnoop_t btsnoop_t;
typedef struct controller_t controller_t;
//typedef struct hci_inject_t hci_inject_t;
typedef struct packet_fragmenter_t packet_fragmenter_t;
//typedef struct vendor_t vendor_t;
//typedef struct low_power_manager_t low_power_manager_t;
//typedef unsigned char * bdaddr_t;
typedef uint16_t command_opcode_t;
/*
typedef enum {
LPM_DISABLE,
LPM_ENABLE,
LPM_WAKE_ASSERT,
LPM_WAKE_DEASSERT
} low_power_command_t;
*/
typedef void (*command_complete_cb)(BT_HDR *response, void *context);
typedef void (*command_status_cb)(uint8_t status, BT_HDR *command, void *context);
typedef struct hci_t {
// Send a low power command, if supported and the low power manager is enabled.
//void (*send_low_power_command)(low_power_command_t command);
// Do the postload sequence (call after the rest of the BT stack initializes).
void (*do_postload)(void);
// Send a command through the HCI layer
void (*transmit_command)(
BT_HDR *command,
command_complete_cb complete_callback,
command_status_cb status_cb,
void *context
);
future_t *(*transmit_command_futured)(BT_HDR *command);
// Send some data downward through the HCI layer
void (*transmit_downward)(uint16_t type, void *data);
} hci_t;
const hci_t *hci_layer_get_interface(void);
int hci_start_up(void);
void hci_shut_down(void);
bool hci_downstream_data_post(uint32_t timeout);
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
int hci_adv_credits_prep_to_release(uint16_t num);
int hci_adv_credits_try_release(uint16_t num);
int hci_adv_credits_force_release(uint16_t num);
#endif
#endif /* _HCI_LAYER_H_ */
@@ -0,0 +1,57 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#ifndef _HCI_PACKET_FACTORY_H_
#define _HCI_PACKET_FACTORY_H_
#include "stack/bt_types.h"
#include "device/event_mask.h"
typedef struct {
BT_HDR *(*make_reset)(void);
BT_HDR *(*make_read_buffer_size)(void);
BT_HDR *(*make_set_c2h_flow_control)(uint8_t enable);
BT_HDR *(*make_set_adv_report_flow_control)(uint8_t enable, uint16_t num, uint16_t lost_threshold);
BT_HDR *(*make_host_buffer_size)(uint16_t acl_size, uint8_t sco_size, uint16_t acl_count, uint16_t sco_count);
BT_HDR *(*make_read_local_version_info)(void);
BT_HDR *(*make_read_bd_addr)(void);
BT_HDR *(*make_read_local_supported_commands)(void);
BT_HDR *(*make_read_local_supported_features)(void);
BT_HDR *(*make_read_local_extended_features)(uint8_t page_number);
BT_HDR *(*make_write_simple_pairing_mode)(uint8_t mode);
BT_HDR *(*make_write_secure_connections_host_support)(uint8_t mode);
BT_HDR *(*make_set_event_mask)(const bt_event_mask_t *event_mask);
BT_HDR *(*make_ble_write_host_support)(uint8_t supported_host, uint8_t simultaneous_host);
BT_HDR *(*make_ble_read_white_list_size)(void);
BT_HDR *(*make_ble_read_buffer_size)(void);
BT_HDR *(*make_ble_read_supported_states)(void);
BT_HDR *(*make_ble_read_local_supported_features)(void);
BT_HDR *(*make_ble_read_resolving_list_size)(void);
#if (BLE_50_FEATURE_SUPPORT == TRUE)
BT_HDR *(*make_read_max_adv_data_len)(void);
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
BT_HDR *(*make_ble_read_suggested_default_data_length)(void);
BT_HDR *(*make_ble_write_suggested_default_data_length)(uint16_t SuggestedMaxTxOctets, uint16_t SuggestedMaxTxTime);
BT_HDR *(*make_ble_set_event_mask)(const bt_event_mask_t *event_mask);
BT_HDR *(*make_write_sync_flow_control_enable)(uint8_t enable);
BT_HDR *(*make_write_default_erroneous_data_report)(uint8_t enable);
} hci_packet_factory_t;
const hci_packet_factory_t *hci_packet_factory_get_interface(void);
#endif /*_HCI_PACKET_FACTORY_H_*/
@@ -0,0 +1,112 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#ifndef _HCI_PACKET_PARSER_H_
#define _HCI_PACKET_PARSER_H_
#include <stdint.h>
#include "osi/allocator.h"
#include "device/bdaddr.h"
#include "stack/bt_types.h"
#include "device/device_features.h"
//#include "features.h"
#include "device/version.h"
typedef struct {
void (*parse_generic_command_complete)(BT_HDR *response);
void (*parse_read_buffer_size_response)(
BT_HDR *response,
uint16_t *acl_data_size_ptr,
uint16_t *acl_buffer_count_ptr,
uint8_t *sco_data_size_ptr,
uint16_t *sco_buffer_count_ptr
);
void (*parse_read_local_version_info_response)(
BT_HDR *response,
bt_version_t *bt_version_ptr
);
void (*parse_read_bd_addr_response)(
BT_HDR *response,
bt_bdaddr_t *address_ptr
);
void (*parse_read_local_supported_commands_response)(
BT_HDR *response,
uint8_t *supported_commands_ptr,
size_t supported_commands_length
);
void (*parse_read_local_supported_features_response)(
BT_HDR *response,
bt_device_features_t *feature_pages
);
void (*parse_read_local_extended_features_response)(
BT_HDR *response,
uint8_t *page_number_ptr,
uint8_t *max_page_number_ptr,
bt_device_features_t *feature_pages,
size_t feature_pages_count
);
void (*parse_ble_read_white_list_size_response)(
BT_HDR *response,
uint8_t *white_list_size_ptr
);
void (*parse_ble_read_buffer_size_response)(
BT_HDR *response,
uint16_t *data_size_ptr,
uint8_t *acl_buffer_count_ptr
);
void (*parse_ble_read_supported_states_response)(
BT_HDR *response,
uint8_t *supported_states,
size_t supported_states_size
);
void (*parse_ble_read_local_supported_features_response)(
BT_HDR *response,
bt_device_features_t *supported_features
);
void (*parse_ble_read_resolving_list_size_response) (
BT_HDR *response,
uint8_t *resolving_list_size_ptr
);
#if (BLE_50_FEATURE_SUPPORT == TRUE)
void (*parse_ble_read_adv_max_len_response) (
BT_HDR *respone,
uint16_t *ble_ext_adv_data_max_len_ptr
);
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
void (*parse_ble_read_suggested_default_data_length_response)(
BT_HDR *response,
uint16_t *ble_default_packet_length_ptr,
uint16_t *ble_default_packet_txtime_ptr
);
} hci_packet_parser_t;
const hci_packet_parser_t *hci_packet_parser_get_interface(void);
#endif /*_HCI_PACKET_PARSER_H_*/
@@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __HCI_TRANS_INT_H__
#define __HCI_TRANS_INT_H__
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "esp_bluedroid_hci.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief host checks whether it can send data to controller
*
* @return true if host can send data, false otherwise
*/
bool hci_host_check_send_available(void);
/**
* @brief host sends packet to controller
*
* @param[in] data pointer to data buffer
* @param[in] len length of data in byte
*/
void hci_host_send_packet(uint8_t *data, uint16_t len);
/**
* @brief register the HCI function interface
*
* @param[in] callback HCI function interface
*
* @return ESP_OK register successfully, ESP_FAIL otherwise
*/
esp_err_t hci_host_register_callback(const esp_bluedroid_hci_driver_callbacks_t *callback);
#ifdef __cplusplus
}
#endif
#endif /* __HCI_TRANS_INT_H__ */
@@ -0,0 +1,62 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#ifndef _PACKET_FRAGMENTER_H_
#define _PACKET_FRAGMENTER_H_
#include "osi/allocator.h"
#include "stack/bt_types.h"
#include "hci/hci_layer.h"
typedef void (*transmit_finished_cb)(BT_HDR *packet, bool all_fragments_sent);
typedef void (*packet_reassembled_cb)(BT_HDR *packet);
typedef void (*packet_fragmented_cb)(BT_HDR *packet, bool send_transmit_finished);
typedef struct {
// Called for every packet fragment.
packet_fragmented_cb fragmented;
// Called for every completely reassembled packet.
packet_reassembled_cb reassembled;
// Called when the fragmenter finishes sending all requested fragments,
// but the packet has not been entirely sent.
transmit_finished_cb transmit_finished;
} packet_fragmenter_callbacks_t;
typedef struct packet_fragmenter_t {
// Initialize the fragmenter, specifying the |result_callbacks|.
void (*init)(const packet_fragmenter_callbacks_t *result_callbacks);
// Release all resources associated with the fragmenter.
void (*cleanup)(void);
// Check if Current fragmenter is ongoing
BT_HDR *(*fragment_current_packet)(void);
// Fragments |packet| if necessary and hands off everything to the fragmented callback.
void (*fragment_and_dispatch)(BT_HDR *packet);
// If |packet| is a complete packet, forwards to the reassembled callback. Otherwise
// holds onto it until all fragments arrive, at which point the reassembled callback is called
// with the reassembled data.
void (*reassemble_and_dispatch)(BT_HDR *packet);
} packet_fragmenter_t;
const packet_fragmenter_t *packet_fragmenter_get_interface(void);
#endif /* _PACKET_FRAGMENTER_H_ */
@@ -0,0 +1,238 @@
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#include <string.h>
#include "common/bt_trace.h"
#include "common/bt_defs.h"
#include "device/controller.h"
#include "hci/hci_internals.h"
#include "hci/hci_layer.h"
#include "hci/packet_fragmenter.h"
#include "osi/hash_map.h"
#include "osi/hash_functions.h"
#include "common/bt_trace.h"
#define APPLY_CONTINUATION_FLAG(handle) (((handle) & 0xCFFF) | 0x1000)
#define APPLY_START_FLAG(handle) (((handle) & 0xCFFF) | 0x2000)
#define SUB_EVENT(event) ((event) & MSG_SUB_EVT_MASK)
#define GET_BOUNDARY_FLAG(handle) (((handle) >> 12) & 0x0003)
#define HANDLE_MASK 0x0FFF
#define START_PACKET_BOUNDARY 2
#define CONTINUATION_PACKET_BOUNDARY 1
#define L2CAP_HEADER_SIZE 4
// TODO(zachoverflow): find good value for this
#define NUMBER_OF_BUCKETS 42
// Our interface and callbacks
static const packet_fragmenter_t interface;
static const controller_t *controller;
static const packet_fragmenter_callbacks_t *callbacks;
static hash_map_t *partial_packets;
static BT_HDR *current_fragment_packet;
static void init(const packet_fragmenter_callbacks_t *result_callbacks)
{
current_fragment_packet = NULL;
callbacks = result_callbacks;
partial_packets = hash_map_new(NUMBER_OF_BUCKETS, hash_function_naive, NULL, NULL, NULL);
}
static void cleanup(void)
{
if (partial_packets) {
hash_map_free(partial_packets);
}
}
static BT_HDR *fragment_get_current_packet(void)
{
return current_fragment_packet;
}
static void fragment_and_dispatch(BT_HDR *packet)
{
uint16_t continuation_handle;
uint16_t max_data_size, max_packet_size, remaining_length;
uint16_t event = packet->event & MSG_EVT_MASK;
uint8_t *stream = packet->data + packet->offset;
assert(packet != NULL);
// We only fragment ACL packets
if (event != MSG_STACK_TO_HC_HCI_ACL) {
callbacks->fragmented(packet, true);
return;
}
max_data_size =
SUB_EVENT(packet->event) == LOCAL_BR_EDR_CONTROLLER_ID ?
controller->get_acl_data_size_classic() :
controller->get_acl_data_size_ble();
max_packet_size = max_data_size + HCI_ACL_PREAMBLE_SIZE;
if((packet->len > max_packet_size) && (packet->layer_specific == 0) && (event == MSG_STACK_TO_HC_HCI_ACL)) {
packet->event = MSG_HC_TO_STACK_L2C_SEG_XMIT;
current_fragment_packet = NULL;
callbacks->transmit_finished(packet, false);
return;
}
remaining_length = packet->len;
STREAM_TO_UINT16(continuation_handle, stream);
continuation_handle = APPLY_CONTINUATION_FLAG(continuation_handle);
if (remaining_length > max_packet_size) {
current_fragment_packet = packet;
UINT16_TO_STREAM(stream, max_data_size);
packet->len = max_packet_size;
callbacks->fragmented(packet, false);
packet->offset += max_data_size;
remaining_length -= max_data_size;
packet->len = remaining_length;
// Write the ACL header for the next fragment
stream = packet->data + packet->offset;
UINT16_TO_STREAM(stream, continuation_handle);
UINT16_TO_STREAM(stream, remaining_length - HCI_ACL_PREAMBLE_SIZE);
// Apparently L2CAP can set layer_specific to a max number of segments to transmit
if (packet->layer_specific) {
packet->layer_specific--;
if (packet->layer_specific == 0) {
packet->event = MSG_HC_TO_STACK_L2C_SEG_XMIT;
/* The remain packet will send back to the l2cap layer when controller buffer is not enough
current_fragment_packet must be NULL, otherwise hci_host_thread_handler() will
connitue handle the remain packet. then the remain packet will be freed.
*/
current_fragment_packet = NULL;
callbacks->transmit_finished(packet, false);
return;
}
}
} else {
current_fragment_packet = NULL;
callbacks->fragmented(packet, true);
}
}
static void reassemble_and_dispatch(BT_HDR *packet)
{
HCI_TRACE_DEBUG("reassemble_and_dispatch\n");
if ((packet->event & MSG_EVT_MASK) == MSG_HC_TO_STACK_HCI_ACL) {
uint8_t *stream = packet->data + packet->offset;
uint16_t handle;
uint16_t l2cap_length;
uint16_t acl_length __attribute__((unused));
STREAM_TO_UINT16(handle, stream);
STREAM_TO_UINT16(acl_length, stream);
STREAM_TO_UINT16(l2cap_length, stream);
assert(acl_length == packet->len - HCI_ACL_PREAMBLE_SIZE);
uint8_t boundary_flag = GET_BOUNDARY_FLAG(handle);
handle = handle & HANDLE_MASK;
BT_HDR *partial_packet = (BT_HDR *)hash_map_get(partial_packets, (void *)(uintptr_t)handle);
if (boundary_flag == START_PACKET_BOUNDARY) {
if (partial_packet) {
HCI_TRACE_WARNING("%s found unfinished packet for handle with start packet. Dropping old.\n", __func__);
hash_map_erase(partial_packets, (void *)(uintptr_t)handle);
osi_free(partial_packet);
}
uint16_t full_length = l2cap_length + L2CAP_HEADER_SIZE + HCI_ACL_PREAMBLE_SIZE;
if (full_length <= packet->len) {
if (full_length < packet->len) {
HCI_TRACE_WARNING("%s found l2cap full length %d less than the hci length %d.\n", __func__, l2cap_length, packet->len);
}
callbacks->reassembled(packet);
return;
}
partial_packet = (BT_HDR *)osi_calloc(full_length + sizeof(BT_HDR));
partial_packet->event = packet->event;
partial_packet->len = full_length;
partial_packet->offset = packet->len;
memcpy(partial_packet->data, packet->data + packet->offset, packet->len);
// Update the ACL data size to indicate the full expected length
stream = partial_packet->data;
STREAM_SKIP_UINT16(stream); // skip the handle
UINT16_TO_STREAM(stream, full_length - HCI_ACL_PREAMBLE_SIZE);
hash_map_set(partial_packets, (void *)(uintptr_t)handle, partial_packet);
// Free the old packet buffer, since we don't need it anymore
osi_free(packet);
} else {
if (!partial_packet) {
HCI_TRACE_ERROR("%s got continuation for unknown packet. Dropping it.\n", __func__);
osi_free(packet);
return;
}
packet->offset += HCI_ACL_PREAMBLE_SIZE; // skip ACL preamble
packet->len -= HCI_ACL_PREAMBLE_SIZE;
uint16_t projected_offset = partial_packet->offset + packet->len;
if (projected_offset > partial_packet->len) { // len stores the expected length
HCI_TRACE_ERROR("%s got packet which would exceed expected length of %d. Truncating.\n", __func__, partial_packet->len);
packet->len = partial_packet->len - partial_packet->offset;
projected_offset = partial_packet->len;
}
memcpy(
partial_packet->data + partial_packet->offset,
packet->data + packet->offset,
packet->len
);
// Free the old packet buffer, since we don't need it anymore
osi_free(packet);
partial_packet->offset = projected_offset;
if (partial_packet->offset == partial_packet->len) {
hash_map_erase(partial_packets, (void *)(uintptr_t)handle);
partial_packet->offset = 0;
callbacks->reassembled(partial_packet);
}
}
} else {
callbacks->reassembled(packet);
}
}
static const packet_fragmenter_t interface = {
init,
cleanup,
fragment_get_current_packet,
fragment_and_dispatch,
reassemble_and_dispatch
};
const packet_fragmenter_t *packet_fragmenter_get_interface(void)
{
controller = controller_get_interface();
return &interface;
}